Version: 1.1 (supersedes 1.0) Status: Ready to build Type: Open-source Claude Code Skill + community dataset Target runtime: Claude Code (Skills standard, agentskills.io). Portable to other agents that support the standard.
This revision resolves a gap analysis. Each item below is now authoritative; where it conflicts with anything later in the doc, this changelog and the updated section win.
Hard contradictions fixed
- C1 — Scoring curve recalibrated and example/test corrected. The v1.0 log formula scored gig_count=24 at ~65 but the example/test claimed 88, and the curve mis-mapped tiers (LOW boundary scored ~42). The formula is the source of truth; it has been replaced with a piecewise-linear-on-log10 curve (§8.2) anchored so tiers read intuitively. gig_count=24 now scores 82; §8.7 and §15 updated to match.
gig_count=0is a flagged special case, not a top score. - C2 —
benchmarks.local.jsonsingle writer.scrape.pyis the only writer ofbenchmarks.local.json(raw canonical rows).build_benchmarks.pyconsumes it and writes different outputs:pricing-pools.local.jsonanddataset-index.local.json(§7.4, §11). - C3 — Dual license files. Repo ships
LICENSE(MIT, code) andLICENSE-DATA(CC-BY-4.0, data + docs). §11, §16.
Missing specs added
- M1 — Keyword/combo generation defined as an explicit LLM step (§6.2a / FR6a). Candidates are not measurements, so LLM generation is permitted.
- M2 — Path A lookup defined. New
query_dataset.pyderivesgig_count+top_gigsfrom the dataset by keyword match, with amatch_confidence; low/no match → skill asks the user, never fabricates (§6.2 FR3, §8A). - M3 —
analyze_pricing.pyper-tier semantics specified: percentiles are computed within each tier's own distribution, with a defined percentile method (§8.6). - M4 — Schemas added for
scoring-config.json(§8.8) andcategories.json(§7.5). - M5 — Field provenance defined (§7.6): which gig fields come from scripts vs the LLM. The determinism rule is scoped to market measurements, not the seller's own offer design.
- M6 — USD FX source defined: a static
fxtable inscoring-config.jsonwith an as-of date (§7.1, §8.8). "No live FX" (§18) stands. - M7 —
contribute.pydependency declared:huggingface_hub(§13). - M8 —
--dry-rundocumented forcontribute.py(FR17, §14 Phase 6, §8C).
Underspecified items resolved
- R1 — KPIs / success metrics added (§5a) as explicit, if soft, targets.
- R2 — Phase 0 and Phase 2 acceptance checks added (§15).
- R3 — Edge cases defined: gig_count=0 and 1≤n<min_samples (§8.2, §8.6).
- R4 —
seller_level↔ experience mapping defined (§7.7); user-stated experience drives the new-seller pricing flag. - R5 — Tier thresholds have one home:
scoring-config.json.keyword-tiers.jsonis removed (§11).
This PRD is written to be one-shot implementable. To build:
- Create an empty directory and
git init. - Save this file as
PRD.mdin the root. - In Claude Code, run:
Read
PRD.mdin full, including the Changelog. Build the entire project following Section 14 (Build Order) top to bottom. Create every file in Section 11 (Repo Structure). Do not skip the scripts. After each phase, run that phase's acceptance check from Section 15 before continuing. Use only the deterministic scoring in Section 8 — never invent competition, demand, or competitor-price numbers anywhere in the skill's instructions or output. When market data is missing, ask the user or state that it isn't available.
North-star rule: every figure the skill reports about market conditions — competition counts, demand, competitor prices — must come from a Python script operating on real input data. The LLM layer never estimates these. (Offer-design fields like delivery days and package contents are the seller's own choices and may be LLM-generated; see §7.6.) If market data is missing, the skill asks the user for it or says it doesn't have it.
A Claude Code Skill named fiverr-gig-optimizer that turns a freelancer's list of services into an optimized, research-backed Fiverr gig catalog: titles, tags, 3-tier pricing, descriptions, thumbnail specs, a phase-based launch plan, and a cross-sell map. All quantitative market recommendations come from deterministic Python scripts — not model guesses.
- Fluff skills. Existing "Fiverr optimizer" repos tell the model to "mentally estimate" competition and prices. That output is hallucinated and unstable. This project replaces guesses with computed values.
- No good free data. No comprehensive, current, all-category Fiverr dataset is freely available. Public Kaggle datasets are stale and niche. This project ships a sample dataset, lets users optionally pull fresh data with their own scraper key, and grows a shared community dataset via opt-in contributions.
- Open-source on GitHub. Code: MIT (
LICENSE). Data + docs: CC-BY-4.0 (LICENSE-DATA). - Installable as a Claude Code plugin via
marketplace.json. - Companion community dataset on the Hugging Face Datasets Hub, grown via opt-in pull requests.
- G1. Produce a complete, copy-paste-ready Fiverr gig catalog from a user's services.
- G2. Base every market figure (competition/demand/pricing) on real data via deterministic scripts.
- G3. Work out-of-the-box for free using a bundled sample dataset (zero API key required) — including a defined keyword→data lookup (§8A).
- G4. Optionally fetch fresh, category-specific data using the user's own scraper API key.
- G5. Optionally let users contribute anonymized data back to a shared public dataset.
- G6. Ship as a clean, properly packaged, installable open-source Claude Code plugin.
- N1. No machine learning. No training, model files, embeddings, or ranking prediction. Scoring is rule-based and auditable.
- N2. No scraping by default. Live scraping is opt-in, runs only with the user's own key, and is the user's responsibility.
- N3. No guessed market numbers. No fallback to model-estimated competition counts or prices.
- N4. Not a Fiverr account automation tool (no posting, refreshing, or messaging bots).
- N5. Not a paid product; no payment layer in v1.
- P1 — Determinism over generation, for market data. Sorting, parsing, scoring, statistics, validation, dedup, and all market measurements live in Python scripts. The model never produces competition/demand/competitor-price numbers. (Offer-design fields are exempt; §7.6.)
- P2 — Progressive disclosure.
SKILL.mdstays lean (target < 400 lines). Detail lives inreferences/and loads only when needed. Scripts are executed, not read into context. - P3 — Honest about data. With no key and no user-supplied counts, the skill uses the sample dataset via
query_dataset.pyand clearly labels coverage, age, and match confidence. It never presents an approximate or stale number as live. - P4 — Privacy by default. Contributions strip all seller-identifying fields (§7.3).
- P5 — Portability. Prefer standard Skills frontmatter fields; keep Claude-Code-specific fields optional.
- P6 — No heavy deps unless justified. Stdlib first.
requestsfor HTTP;huggingface_hubfor contribution;pandasonly where it meaningfully simplifies CSV work. HTML/PDF output reuses headless Chrome.
- U1 — New Fiverr seller with skills but no gigs. Free path (sample data).
- U2 — Existing seller optimizing/expanding. May provide Analytics export and/or a scraper key.
- U3 — Agency / power user scraping multiple categories, willing to contribute data back.
- U4 — Contributor / developer improving the skill or dataset.
- US1. As U1, I describe my services and get 3–4 launch gigs with titles, tags, pricing, and thumbnail specs.
- US2. As any user, competition labels reflect the real number of gigs for a keyword.
- US3. As any user, pricing tiers are benchmarked against real competitor prices.
- US4. As U2, I paste the "X services available" count and the skill scores it — no guessing.
- US5. As U2/U3, I provide my own scraper key and the skill pulls fresh data for my categories.
- US6. As U3, I opt in to contribute anonymized data; I'm credited.
- US7. As any user, I get a single HTML catalog with copy buttons and downloadable thumbnails, plus optional per-gig PDFs.
- US8. As U4,
DATA_POLICY.mdtells me exactly what is collected and stripped.
- K1 — Provenance coverage: 100%. Every market number in any output carries a source label (sample / manual / live) and, for sample data, a match confidence. Verified by the §15 global grep + output inspection.
- K2 — Determinism: 0 guessed market numbers. No prose/template in the skill describes producing competition or price figures without data (enforced in §15).
- K3 — Output lint pass ≥ 90%. Of generated gigs, ≥90% satisfy: title starts "I will", ≤80 chars, exactly 5 tags, description ≤1200 chars. A
lint_gig_config.pycheck (optional) or manual spot-check confirms. - K4 — Dataset growth (aspirational): community dataset reaches a documented row/category milestone over time via contributions (tracked in the HF repo, not enforced by the build).
- FR1. Ask, in one numbered message: name; brand (optional); website (optional); services list; headshot path (optional); existing gig URLs or "none"; monthly revenue goal; experience level (New / L1 / L2 / Top Rated).
- FR2. If fewer than 3 services are given, prompt for more (needed for combo-niche analysis).
- FR6a. From the services list, the model proposes a candidate set of (a) single-service keywords and (b) 2-way combos (e.g. "n8n + AI chatbot"), following naming guidance in
fiverr-seo-playbook.md. These are search candidates, not measurements, so LLM generation is allowed. The candidate list is then scored using real data (§6.2/§6.3). Combos with 3+ services are allowed when the playbook deems them coherent.
- FR3. Path A — Sample data (default, free). For each candidate keyword, run
query_dataset.pyagainstreferences/benchmarks.sample.json(anddataset-index.local.jsonif present) to obtaingig_count,top_gigs, andmatch_confidence(§8A). On low/no match, the skill asks the user to paste a count (Path B) — it never fabricates one. State coverage, the dataset'sgenerated_at, and match confidence in output. - FR4. Path B — Manual counts. For each keyword, ask the user to paste the count Fiverr shows ("X services available"). Never fabricate this number.
- FR5. Path C — Live scrape (opt-in). With the user's scraper key + actor ID,
scrape.pypulls gigs for the target categories intobenchmarks.local.json(raw canonical rows).build_benchmarks.pythen derivespricing-pools.local.jsonanddataset-index.local.jsonfrom it. - FR6. The chosen path and freshness are surfaced in output (e.g. "Competition for 'x': sample-data match, confidence MEDIUM, dataset generated 2025-12; pricing: live scrape 2026-06-25").
- FR7.
score_keyword.pycomputes competition/demand/opportunity scores and a tier label per keyword using only supplied data andscoring-config.json(§8). - FR8. The skill surfaces the script's JSON fields verbatim. No relabeling or invented numbers.
- FR9.
analyze_pricing.pytakes competitor prices for a category and returns p10/p25/median/p75/p90 per tier and a recommended Basic/Standard/Premium triple (§8.6). - FR10. If a tier has fewer than
min_samples(default 8) prices, the script returnsconfidence: "low"for that tier and recommends nothing numeric for it.
- FR11. The skill assembles
gig-config.json(§7.2) from: computed competition tiers/scores, computed pricing, SEO rules (fiverr-seo-playbook.md), and the taxonomy (categories.json). - FR12. Title rules: starts with "I will", ≤80 chars, primary keyword front-loaded. Exactly 5 tags. Description ≤1200 chars, primary keyword in first paragraph. Unique thumbnail accent per gig from the palette.
- FR13. Phase rollout: Phase 1 = 3–4 lowest-competition combos; Phase 2 = 2–3 premium upsells; Phase 3 = 1–2 expansions. Each gig lists 2–3 cross-sell targets.
- FR14.
build_catalog.pyreadsgig-config.jsonand writes a self-containedfiverr-catalog.html(canvas thumbnails 1280×769, copy buttons, per-gig PNG download, cross-sell map, action plan). No scoring inside. - FR15.
build_pdfs.pyoptionally renders one A4 PDF per gig via headless Chrome; skipped gracefully if no Chrome/Edge (warn, don't fail).
- FR16. After a live scrape, ask whether to contribute the anonymized data. Default no; never pressure.
- FR17.
contribute.pystrips PII (§7.3), deduplicates against the dataset, and opens a Hugging Face PR; the user is added toCONTRIBUTORS.md. Supports--dry-run(prints cleaned + deduped records, opens no PR).
- FR18. Model-invocable (auto-triggers on Fiverr phrasing) and runnable as
/fiverr-gig-optimizer.
{
"scraped_at": "2026-06-25",
"category": "Programming & Tech",
"subcategory": "AI Development > AI Chatbots",
"title": "I will build a custom AI chatbot for your website",
"seller_level": "level_two_seller",
"rating": 4.9,
"review_count": 847,
"basic_price": 75,
"standard_price": 199,
"premium_price": 450,
"basic_delivery_days": 3,
"standard_delivery_days": 7,
"premium_delivery_days": 14,
"tags": ["chatbot", "ai", "openai", "langchain", "automation"],
"gig_count_in_search": 1243,
"currency": "USD",
"original_currency": "USD"
}Prices are normalized to USD at ingest using the static fx table in scoring-config.json (§8.8); original_currency records the pre-conversion currency. Missing numerics are null, never zero-filled or guessed. gig_count_in_search is the "X services available" total for the search that produced the row.
{
"seller": { "name": "", "brand": "", "website": "", "photo": "" },
"data_provenance": {
"pricing_source": "sample_dataset|manual|live_scrape",
"pricing_generated_at": "2026-06-25",
"competition_source": "sample_dataset|manual|live_scrape",
"match_confidence": "HIGH|MEDIUM|LOW|null"
},
"strategy": {
"monthlyTarget": "$10,000",
"primaryCategory": "Programming & Tech",
"phase1Count": 4, "phase2Count": 3, "phase3Count": 2
},
"gigs": [
{
"id": 1,
"phase": 1,
"title": "I will ... (<=80 chars)",
"cat": "Programming & Tech > AI Chatbots",
"tags": ["t1","t2","t3","t4","t5"],
"desc": "<=1200 chars",
"competition": { "count": 24, "tier": "LOW", "source": "manual", "match_confidence": null },
"scores": { "competition": 82, "demand": null, "opportunity": 82, "flags": ["demand_unavailable"] },
"xsell": "CROSS-SELLS TO: Gig #2, Gig #3",
"pricing": {
"basic": { "name":"Starter","title":"","price":97,"del":"3 days","rev":"2","items":[] },
"standard": { "name":"Business","title":"","price":247,"del":"6 days","rev":"3","items":[] },
"premium": { "name":"Enterprise","title":"","price":497,"del":"10 days","rev":"5","items":[] }
},
"img": {
"bg1":"#030a0a","bg2":"#061818","accent":"#06b6d4",
"headline":"AI CHATBOT","sub":"Custom GPT for your site",
"badge":"LOW COMPETITION","tools":["OpenAI","LangChain","n8n"],
"pdfWhat":"One-line summary for PDF"
}
}
]
}The renderer treats competition, scores, and pricing as authoritative precomputed inputs and performs no scoring.
Keep: scraped_at, category, subcategory, title, seller_level, rating, review_count, basic_price, standard_price, premium_price, basic_delivery_days, standard_delivery_days, premium_delivery_days, tags, gig_count_in_search, currency, original_currency.
Drop before contributing (hard requirement): seller username, seller display name, profile URL, gig URL, profile photo URL, seller country, any free-text review content, any image URLs, any ID fields.
benchmarks.local.json— written only byscrape.py. Raw canonical gig rows (§7.1) from a live scrape.pricing-pools.local.json— written bybuild_benchmarks.py. Per-category, per-tier price lists consumed byanalyze_pricing.py. Shape:{ "<category/subcategory>": { "basic": [..], "standard": [..], "premium": [..] } }.dataset-index.local.json— written bybuild_benchmarks.py. A keyword/tag →{gig_count, row_ids}index thatquery_dataset.pycan use for faster/fresher Path-A lookups. Optional;query_dataset.pyfalls back to scanningbenchmarks.sample.jsonif absent. All three match*.local.jsonin.gitignoreand are never committed.
Nested object: category → subcategory → list of leaf subcategories.
{
"Programming & Tech": {
"AI Development": ["AI Chatbots", "AI Agents", "AI Applications"],
"Website Development": ["Business Websites", "E-Commerce Development", "Landing Pages"]
},
"Digital Marketing": {
"Search": ["SEO", "Local SEO"],
"Social": ["Social Media Marketing", "Paid Social"]
}
}v1 may ship top categories + key subcategories and expand later. Used for FR11 categorization and to validate the cat field.
The determinism rule (P1) applies to market measurements only. Provenance per gig-config.json field:
| Field group | Source | Why |
|---|---|---|
competition.count, .tier, scores.* |
Script (score_keyword.py / query_dataset.py) |
Market measurement |
pricing.*.price |
Script (analyze_pricing.py) |
Market measurement (competitor prices) |
pricing.*.del (delivery), .rev (revisions), .items |
LLM, from the user's stated capabilities + playbook conventions | Seller's own offer design, not a market measurement |
title, desc, tags |
LLM, following playbook + categories.json |
Copy, constrained by lint rules (FR12) |
img.* (headline, sub, badge, tools, accent, pdfWhat) |
LLM, palette from playbook | Presentation |
data_provenance.* |
Script/orchestration | Truthful labeling |
Delivery days and revisions are numbers but are choices, not observations, so LLM generation does not violate P1. The skill must still keep them realistic and consistent with the user's stated turnaround.
New <-> new_seller
L1 <-> level_one_seller
L2 <-> level_two_seller
Top Rated <-> top_rated_sellerThe user's stated experience drives the new-seller pricing flag (§8.6), not scraped seller levels. Scraped seller_level is used only as demand/quality context.
All constants live in references/scoring-config.json (schema §8.8) so they are transparent and tunable. Defaults below.
Purpose: derive a gig_count and top_gigs for a keyword from the sample/local dataset so the free path can feed score_keyword.py.
CLI: query_dataset.py --keyword "ai chatbot n8n" [--dataset references/benchmarks.sample.json] [--index dataset-index.local.json] [--top-n 10]
Matching logic (deterministic):
- Normalize keyword and each row's
tags,title,subcategoryto lowercase token sets. - For each row, compute a match score = max(Jaccard(keyword_tokens, tag_tokens), subcategory-contains-keyword ? 1.0 : 0, title-token-overlap ratio).
- A row "matches" if match score ≥
lookup.match_threshold(default 0.5). gig_count= thegig_count_in_searchof the best-matching search cluster (mode of matched rows'gig_count_in_search; if they disagree, take the median).top_gigs= matched rows sorted byreview_countdesc, truncated totop_n.match_confidence: HIGH if best match score ≥ 0.8 and ≥lookup.min_rows(default 5) rows matched; MEDIUM if ≥ threshold; LOW if below threshold or fewer thanmin_rows.
Output:
{ "keyword":"ai chatbot n8n", "gig_count": 1243, "match_confidence":"MEDIUM",
"matched_rows": 7, "top_gigs":[ /* canonical rows */ ], "source":"sample_dataset" }If match_confidence == "LOW" or no rows match → return gig_count: null with flags:["no_match"]; the skill then asks the user for a manual count (Path B). No fabrication.
gig_count— integer total gigs for the keyword (fromquery_dataset.py, manual paste, or scrape). Required for competition scoring.- Optional
top_gigs— competitor records for demand. Absent →demand_score = null(not guessed); opportunity falls back to competition-only with a flag.
Piecewise-linear interpolation over log10(gig_count), anchored to the tier structure. Anchors (gig_count → score): (1 → 100), (200 → 70), (2000 → 40), (20000 → 0), clamped to [0, 100].
import math
def competition_score(gig_count, anchors): # anchors = [[1,100],[200,70],[2000,40],[20000,0]]
if gig_count == 0:
return None # special case: no results -> see flags below, not a top score
x = math.log10(max(gig_count, 1))
pts = [(math.log10(c), s) for c, s in anchors]
if x <= pts[0][0]: return round(pts[0][1])
if x >= pts[-1][0]: return round(pts[-1][1])
for (x0, s0), (x1, s1) in zip(pts, pts[1:]):
if x0 <= x <= x1:
t = (x - x0) / (x1 - x0)
return round(s0 + t * (s1 - s0))Worked values: gig_count=24 → 82; 200 → 70; 2000 → 40; 10000 → 12. (24: log10≈1.380, t=1.380/2.301=0.600, 100−0.600×30=82.)
Edge case gig_count == 0: competition_score = null, add flag "no_results", and set tier = "UNTESTED". Opportunity for an untested niche is null with flag "untested_niche" (no demand evidence either way) — not a perfect score.
Tier from gig_count (thresholds in scoring-config.json):
UNTESTED: gig_count == 0
LOW: 0 < gig_count < 200
MEDIUM: 200 <= gig_count < 2000
HIGH: gig_count >= 2000Proxy = median review_count of the top N (default 10) competitor gigs.
DEMAND_CEILING = 500
demand_score = round(clamp((median_top_reviews / DEMAND_CEILING) * 100, 0, 100))If top_gigs unavailable → demand_score = null.
if tier == "UNTESTED": opportunity = null; flag "untested_niche"
elif demand_score is not null: opportunity = round(0.6*competition_score + 0.4*demand_score)
else: opportunity = competition_score; flag "demand_unavailable"Weights live in config; rationale documented in README so users can audit/adjust.
If a keyword is a 2+ word combo with gig_count below the LOW threshold, add combo_bonus (default 0; opt-in +10) to opportunity, capped at 100. Off by default to keep scores literal.
Per-tier semantics: percentiles are computed within each tier's own price distribution — p25 of the basic prices, median of the standard prices, p75 of the premium prices — not from a merged pool.
Percentile method (defined):
def percentile(values, p): # p in [0,100]
s = sorted(values)
if len(s) == 1: return s[0]
# statistics.quantiles inclusive, n=100, then index by p (1..99); clamp ends
import statistics
if p <= 0: return s[0]
if p >= 100: return s[-1]
q = statistics.quantiles(s, n=100, method="inclusive") # 99 cut points
return q[p-1]Recommendation:
default:
basic = percentile(basic_prices, 25)
standard = percentile(standard_prices, 50)
premium = percentile(premium_prices, 75)
new-seller strategy (user experience == "New"):
basic = percentile(basic_prices, 10)
standard = percentile(standard_prices, 25)
premium = percentile(premium_prices, 50)Confidence: computed per tier. If a tier has n < min_samples (default 8) → that tier returns {"confidence":"low","reason":"insufficient samples (n=<k>)"} and recommends nothing numeric for it. If 1 <= n < min_samples, still report the raw percentile but flag low confidence; if n == 0, recommend nothing for that tier.
{
"keyword": "ai chatbot n8n",
"gig_count": 24,
"competition_score": 82,
"tier": "LOW",
"demand_score": null,
"opportunity_score": 82,
"flags": ["demand_unavailable"]
}The skill surfaces these fields verbatim.
{
"competition": {
"anchors": [[1, 100], [200, 70], [2000, 40], [20000, 0]]
},
"tiers": { "low_max": 200, "medium_max": 2000 },
"demand": { "ceiling": 500, "top_n": 10 },
"opportunity": { "w_competition": 0.6, "w_demand": 0.4, "combo_bonus": 0 },
"pricing": {
"min_samples": 8,
"default": { "basic_p": 25, "standard_p": 50, "premium_p": 75 },
"new_seller": { "basic_p": 10, "standard_p": 25, "premium_p": 50 }
},
"lookup": { "match_threshold": 0.5, "min_rows": 5, "top_n": 10 },
"scraper": { "base_url": "https://api.apify.com/v2", "actor_id": "<actor-id-here>" },
"dataset_repo": "https://huggingface.co/datasets/<owner>/fiverr-gigs",
"fx": {
"base": "USD",
"rates_as_of": "2026-06-01",
"rates": { "USD": 1.0, "EUR": 1.08, "GBP": 1.27, "INR": 0.012, "PKR": 0.0036 }
}
}Tier thresholds live only here (no separate keyword-tiers.json). FX is a static table with an explicit rates_as_of; scrape.py converts non-USD prices to USD on ingest by dividing/multiplying via this table. (§18 "no live FX" stands.)
- Seed (
benchmarks.sample.json). Built by merging the maintainer's public Kaggle Fiverr datasets into the canonical schema (§7.1), withscraped_at/generated_atset to each source's date.scripts/seed/merge_kaggle.py+seed/README.mddocument the merge and dates. Coverage is partial and dated — labeled as such everywhere it's used. - Live (
scrape.py). Uses an Apify-compatible scraping API with the user's own key and a configurableactor_id. Costs are the user's. The skill never ships a key and never scrapes without one. - Community dataset (Hugging Face). Public, CC-BY-4.0, grown via opt-in PRs from
contribute.py. Repo path set inscoring-config.json → dataset_repo. DATA_POLICY.mdmust state: only public Fiverr listing data is handled; exact keep/strip fields (§7.3); contribution is opt-in and CC-BY-4.0; live scraping is the user's responsibility under Fiverr's ToS; no private account data is stored.
---
name: fiverr-gig-optimizer
description: >
Generates a research-backed Fiverr gig catalog — titles, tags, 3-tier
pricing, descriptions, thumbnail specs, and a phase-based launch plan —
using deterministic Python scripts for all competition and pricing numbers.
Use when the user asks to optimize a Fiverr gig, research a Fiverr keyword,
check gig competition, price a gig, or plan a Fiverr launch.
allowed-tools: Bash(python3 *) Read Write
argument-hint: "[service or keyword]"
metadata:
version: "1.1"
license: MIT
---Constraints: name ≤64 chars, lowercase/numbers/hyphens only; description ≤1024 chars; no XML tags; no reserved words ("anthropic", "claude").
## Overview— one paragraph; restate the determinism-for-market-data rule.## When to use— trigger phrases.## Hard rules— never invent competition/demand/competitor-price numbers; always run the scoring scripts; on missing market data, ask or say "I don't have that"; offer-design fields (delivery, revisions, items) may be authored but kept realistic; strip PII before any contribution.## Workflow— Steps 1–7 mapping to FR1–FR17, each naming the exact script viapython3 ${CLAUDE_SKILL_DIR}/scripts/<name>.py ...and the reference file to read. Include the keyword-generation step (FR6a) and the Path-Aquery_dataset.pystep (FR3).## Output format— present scores verbatim from JSON; always include a provenance line; where files are written.## Error handling— no key → offer manual/sample paths; low match confidence → ask for a manual count; insufficient pricing samples → low-confidence flag; no Chrome → skip PDFs with a warning.## Examples— (a) a full run on sample data with a MEDIUM-confidence match; (b) a run wherequery_dataset.pyreturnsno_matchand the skill asks for a count instead of guessing.
Keep the body < 400 lines. Push the full SEO ruleset and color palette into references/fiverr-seo-playbook.md.
fiverr-gig-optimizer/
├── .claude-plugin/
│ ├── plugin.json
│ └── marketplace.json
├── skills/
│ └── fiverr-gig-optimizer/
│ ├── SKILL.md
│ ├── scripts/
│ │ ├── score_keyword.py # §8.2–8.5, output §8.7
│ │ ├── query_dataset.py # §8A (Path A lookup) — NEW
│ │ ├── analyze_pricing.py # §8.6
│ │ ├── scrape.py # FR5; writes benchmarks.local.json only
│ │ ├── build_benchmarks.py # writes pricing-pools.local.json + dataset-index.local.json
│ │ ├── contribute.py # FR17 (+ --dry-run); PII strip + dedup + HF PR
│ │ ├── build_catalog.py # FR14 (HTML; no scoring)
│ │ ├── build_pdfs.py # FR15 (headless Chrome; optional)
│ │ └── seed/
│ │ ├── merge_kaggle.py
│ │ └── README.md
│ ├── references/
│ │ ├── benchmarks.sample.json # bundled seed (canonical schema)
│ │ ├── scoring-config.json # §8.8 — sole home for thresholds + fx + config
│ │ ├── categories.json # §7.5
│ │ └── fiverr-seo-playbook.md # title/tag/desc rules + 8-color palette
│ └── assets/
│ └── pdf-template.html
├── tests/
│ ├── test_score_keyword.py
│ ├── test_query_dataset.py # NEW
│ └── test_analyze_pricing.py
├── examples/
│ └── sample-gig-config.json
├── DATA_POLICY.md
├── CONTRIBUTORS.md
├── LICENSE # MIT (code)
├── LICENSE-DATA # CC-BY-4.0 (data + docs) — NEW
├── requirements.txt
├── .gitignore # *.local.json, .env, __pycache__
└── README.md(keyword-tiers.json removed — thresholds live in scoring-config.json.)
{
"name": "fiverr-gig-optimizer",
"version": "1.1.0",
"description": "Research-backed Fiverr gig optimizer. Deterministic competition + pricing scoring, no ML, no guessed market numbers.",
"author": { "name": "<maintainer>" },
"license": "MIT",
"keywords": ["fiverr", "freelance", "gig", "pricing", "seo", "claude-code", "skill"]
}{
"name": "fiverr-tools",
"owner": "<github-username>",
"plugins": [
{
"name": "fiverr-gig-optimizer",
"source": "./",
"description": "Research-backed Fiverr gig optimizer (Claude Code skill)."
}
]
}Rules: kebab-case names; name must NOT be a reserved Anthropic marketplace name (claude-code-plugins, anthropic-plugins, agent-skills). Show pinning by tag/SHA in docs. Validate with claude plugin validate ..
/plugin marketplace add <github-username>/fiverr-gig-optimizer
/plugin install fiverr-gig-optimizer@fiverr-tools- Python 3.8+. Stdlib:
json, csv, statistics, math, hashlib, argparse, urllib, os, sys, datetime. - Third-party:
requests>=2.31(HTTP for scrape);huggingface_hub>=0.23(contribution PRs);pandas>=2.0only insideseed/merge_kaggle.py/build_benchmarks.pyif it simplifies CSV merging (else stdlibcsv). - Optional runtime: Google Chrome or Microsoft Edge for
build_pdfs.py(auto-detect; skip if absent). - Scraper: Apify-compatible token via
--api-keyorAPIFY_TOKEN;actor_idfromscoring-config.json. - Contribution: Hugging Face token (contributors only).
- Create
requirements.txtlisting exactly:requests>=2.31,huggingface_hub>=0.23, andpandas>=2.0if used.
Phase 0 — Scaffold. Repo tree (§11); LICENSE (MIT); LICENSE-DATA (CC-BY-4.0); .gitignore; empty CONTRIBUTORS.md; requirements.txt.
Phase 1 — Scoring core.
references/scoring-config.json(§8.8).score_keyword.py(§8.2–8.5; output §8.7). CLI:--keyword "x" --gig-count N [--top-gigs path.json].analyze_pricing.py(§8.6). CLI:--prices path.json --experience New.query_dataset.py(§8A). CLI:--keyword "x" [--dataset ...] [--index ...] [--top-n 10].- Tests:
test_score_keyword.py,test_query_dataset.py,test_analyze_pricing.py— assert exact outputs (e.g. gig_count=24 → competition_score=82, tier=LOW).
Phase 2 — Reference data.
6. categories.json (§7.5).
7. fiverr-seo-playbook.md (rules + 8-color palette).
8. seed/merge_kaggle.py + seed/README.md; produce benchmarks.sample.json (a few real, schema-valid rows acceptable for first build; document regeneration).
Phase 3 — Skill definition.
9. SKILL.md (§10) wiring Steps 1–7 to scripts/references, including FR6a and the Path-A step.
10. examples/sample-gig-config.json (§7.2).
Phase 4 — Data acquisition (opt-in).
11. scrape.py — call the configured actor with the user's key; map results to canonical schema (§7.1), convert currencies via fx; write benchmarks.local.json. Handle 429/auth with clear messages.
12. build_benchmarks.py — from benchmarks.local.json (or a user CSV) write pricing-pools.local.json + dataset-index.local.json.
Phase 5 — Output rendering.
13. build_catalog.py — gig-config.json → fiverr-catalog.html (no scoring).
14. build_pdfs.py — optional per-gig A4 PDFs via headless Chrome; skip gracefully.
Phase 6 — Contribution.
15. contribute.py — PII strip (§7.3), dedup (hash on title|category), HF PR; append contributor to CONTRIBUTORS.md; support --dry-run.
16. DATA_POLICY.md (§9).
Phase 7 — Packaging + docs.
17. .claude-plugin/plugin.json + marketplace.json (§12); run claude plugin validate ..
18. README.md (§16).
- Phase 0 — NEW. Repo tree matches §11;
claudeloads in the directory with no error; bothLICENSEandLICENSE-DATAexist;requirements.txtpresent. - Phase 1.
score_keyword.py --keyword "ai chatbot n8n" --gig-count 24→competition_score: 82, tier: "LOW", demand_score: null, opportunity_score: 82.--gig-count 0→competition_score: null, tier: "UNTESTED", opportunity_score: null, flags:["no_results","untested_niche"].analyze_pricing.pyon a 12-price tier returns p25/median/p75; on a 5-price tier returnsconfidence:"low"for that tier.query_dataset.pyon a keyword present in the sample returns agig_count+match_confidence; on a nonsense keyword returnsgig_count:null, flags:["no_match"]. All three test files pass. - Phase 2 — NEW checks added.
benchmarks.sample.jsonvalidates against the canonical schema (every row has the required keys; numerics are number-or-null);categories.jsonparses as the §7.5 shape;scoring-config.jsonparses and contains all keys in §8.8. - Phase 3. Installed locally, "help me optimize my Fiverr gigs for n8n automation" auto-triggers the skill;
/fiverr-gig-optimizeralso works. With no key, the skill runsquery_dataset.py; onno_matchit asks for a count and states no competition number it wasn't given.claude --debugshows no YAML error;/doctorshows the description not truncated/dropped. - Phase 4. With a valid key,
scrape.pywrites ≥1 valid canonical row (USD-normalized,original_currencyset); with an invalid key it prints a clear auth error and exits non-zero (no crash, no fabricated data).build_benchmarks.pyproduces bothpricing-pools.local.jsonanddataset-index.local.json. - Phase 5.
build_catalog.pyonexamples/sample-gig-config.jsonproduces one HTML file that opens with working copy buttons and ≥1 rendered thumbnail.build_pdfs.pywith no browser warns and exits 0. - Phase 6.
contribute.py --dry-runon a scraped file outputs cleaned, deduped records with zero PII fields (assert none of the §7.3 drop-keys appear). - Phase 7.
claude plugin validate .passes; README install commands are correct and copy-pasteable.
Global acceptance. Grep the final SKILL.md and all prose templates for market-guessing language ("estimate", "mentally", "approximately", "I think the competition") — there must be none describing how to produce market numbers without data. (Offer-design language about delivery/revisions is fine.)
Include: one-paragraph pitch; the determinism principle scoped to market data (why this isn't a fluff tool); install commands (§12.3); the three data paths (sample/manual/live) with the honest note that sample data is partial, dated, and surfaced with a match confidence; how to get and pass a scraper key; how to contribute and what gets stripped (link DATA_POLICY.md); the scoring formula (anchors, weights) and how to tune scoring-config.json; a "no ML, no auto-scraping, no guessed market numbers" statement; license split — code MIT (LICENSE), data + docs CC-BY-4.0 (LICENSE-DATA); link to the Hugging Face dataset; troubleshooting (no Chrome, 429s, low-confidence pricing, low match confidence).
- Fiverr has no public data API; its
robots.txtdisallows search paths; it uses anti-bot protection. The skill does not scrape by default and never ships credentials. - Live scraping runs only with the user's own key and is the user's responsibility under Fiverr's ToS and applicable law. State this in README and
DATA_POLICY.md. - Only public listing metadata is handled or shared; all seller-identifying fields are stripped before contribution (§7.3).
- This is not legal advice; the project takes no position on the legality of scraping in any jurisdiction and asks users to verify their own compliance.
- Ranking prediction or any ML model (excluded in v1).
- Auto-publishing gigs, messaging, or account automation.
- A hosted web UI or paid marketplace listing.
- Live FX. v1 uses the static
fxtable (§8.8) with a documentedrates_as_of; live currency conversion is out of scope. - Auto-refreshing the bundled sample data on a schedule (v1 is manual via
seed/).
- Skills load via progressive disclosure: only
name+description(~100 tokens) preload; the body loads on trigger;scripts/,references/,assets/load only when pointed to. KeepSKILL.mdlean. - Reference scripts as
${CLAUDE_SKILL_DIR}/scripts/<name>.pyso paths resolve wherever installed. - Pre-approve tools with
allowed-tools: Bash(python3 *) Read Write. - Every script emits JSON to stdout; the skill surfaces market fields verbatim.
- Authoritative references:
code.claude.com/docs/en/skills,platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices,agentskills.io/specification, and theanthropics/skills+anthropics/claude-code(plugin-dev / skill-development) repos. - Treat version-specific limits (token/line caps, frontmatter fields) as current-but-mutable; prefer documented standard fields for portability.