@clawhub-harrylabsj-35a31b2850
Optimize ecommerce product listings for Amazon A9, Taobao/1688 search, JD search, TikTok Shop discovery, Xiaohongshu SEO, and Shopify storefront search using...
---
name: search-listing-optimizer
description: Optimize ecommerce product listings for Amazon A9, Taobao/1688 search, JD search, TikTok Shop discovery, Xiaohongshu SEO, and Shopify storefront search using keyword density frameworks, attribute-completeness checklists, and conversion-trap detection. Use when sellers need a listing audit, keyword refresh, or conversion improvement brief without live search analytics or platform API access.
---
# Search Listing Optimizer
## Overview
Use this skill to audit a product listing and generate an optimization brief covering keyword targeting, attribute completeness, image metadata, and conversion-trap detection. It applies built-in search-ranking heuristics and best-practice libraries to surface actionable improvements.
This MVP is heuristic. It does **not** connect to live search analytics, A9 algorithms, or platform seller portals. It relies on the user's provided listing notes and product context.
## Trigger
Use this skill when the user wants to:
- audit an Amazon, Taobao, JD, TikTok Shop, Xiaohongshu, or Shopify listing for search visibility
- identify missing keywords, weak attributes, or conversion traps in existing copy
- rebuild a listing keyword strategy for a new product or category entry
- compare listing quality across multiple marketplaces
- generate a before-and-after optimization brief for a product team
### Example prompts
- "Audit our Amazon skincare listing for A9 visibility"
- "Help me optimize our Taobao product listing for crowd-collapse ranking"
- "What's wrong with our JD listing that gets traffic but no conversions?"
- "Create an optimization brief for our new TikTok Shop product"
## Workflow
1. Capture the platform, product context, and optimization goal.
2. Apply keyword-density and search-intent frameworks for the target platform.
3. Assess attribute completeness across key fields.
4. Detect common conversion traps (pricing, images, reviews, description).
5. Return a markdown optimization brief with prioritized action items.
## Inputs
The user can provide any mix of:
- platform: Amazon, Taobao, 1688, JD, TikTok Shop, Xiaohongshu, Shopify, general
- product category: category name, subcategory, price range
- current listing notes: title, bullet points, description, keywords, images, reviews
- optimization goal: visibility boost, conversion improvement, new listing, relaunch
- competitor context: competitor ASINs, titles, or keyword strategies (optional)
## Outputs
Return a markdown brief with:
- platform search algorithm summary (how ranking works on that platform)
- keyword opportunity map (search volume intent × competition)
- listing attribute scorecard (title, bullets, description, backend keywords, images)
- conversion-trap detection (pricing, trust signals, review depth, description clarity)
- prioritized action list with expected impact
- template for revised title, bullets, or description
## Safety
- No live search analytics, ad console, or seller portal access.
- Optimization recommendations are directional; actual ranking depends on platform algorithms.
- Do not claim guaranteed ranking improvements or sales lift.
- Pricing and promotional decisions remain with the operator.
## Best-fit Scenarios
- sellers preparing new listings or refreshing existing ones for better visibility
- teams managing multiple marketplace accounts needing a consistent audit framework
- brands expanding to new platforms and needing a listing best-practice brief
## Not Ideal For
- real-time rank tracking, automated listing updates, or live ad campaign management
- products in highly regulated categories with strict claim restrictions
- very early-stage products with no sales history or review data
## Acceptance Criteria
- Return markdown text.
- Include attribute scorecard, keyword opportunity map, and prioritized action list.
- Make ranking assumptions explicit.
- Keep the brief practical for marketplace sellers and listing specialists.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import Any, Dict, List, Union
PLATFORM_RULES = {
'Amazon': ['amazon', 'a9', 'seller central', 'asin'],
'Taobao / 1688': ['taobao', '1688'],
'JD': ['jd', 'jingdong'],
'TikTok Shop': ['tiktok', 'tik tok'],
'Xiaohongshu': ['xiaohongshu', 'rednote', 'xhs'],
'Shopify': ['shopify', 'storefront'],
'General': ['general', 'multi', 'all platform'],
}
ATTRIBUTE_WEIGHT = {
'Title': {'amazon': 25, 'taobao': 20, 'jd': 20, 'tiktok': 15, 'xiaohongshu': 10, 'shopify': 20, 'general': 15},
'Bullets / Key Features': {'amazon': 25, 'taobao': 20, 'jd': 15, 'tiktok': 15, 'xiaohongshu': 15, 'shopify': 20, 'general': 15},
'Description / Body': {'amazon': 15, 'taobao': 20, 'jd': 20, 'tiktok': 20, 'xiaohongshu': 20, 'shopify': 20, 'general': 20},
'Backend Keywords': {'amazon': 20, 'taobao': 15, 'jd': 15, 'tiktok': 10, 'xiaohongshu': 5, 'shopify': 10, 'general': 10},
'Images': {'amazon': 10, 'taobao': 15, 'jd': 15, 'tiktok': 20, 'xiaohongshu': 25, 'shopify': 15, 'general': 20},
'Reviews / Rating': {'amazon': 5, 'taobao': 5, 'jd': 10, 'tiktok': 15, 'xiaohongshu': 20, 'shopify': 15, 'general': 10},
'Price': {'amazon': 0, 'taobao': 5, 'jd': 5, 'tiktok': 5, 'xiaohongshu': 5, 'shopify': 5, 'general': 5},
}
CONVERSION_TRAPS = {
'Pricing': ['price too high', 'price mismatch', 'not competitive', 'expensive'],
'Images': ['no main image', 'blurry', 'no lifestyle', 'no infographic', 'missing alt text'],
'Reviews': ['no reviews', 'low rating', 'negative reviews', 'review count low'],
'Description': ['vague', 'missing spec', 'no benefit', 'hard to read', 'no proof points'],
'Trust Signals': ['no guarantee', 'no return policy visible', 'no FAQ', 'no brand story'],
}
ListingInput = Union[str, Dict[str, Any]]
def _score_rules(text: str, rules: Dict[str, List[str]]) -> Dict[str, int]:
return {name: sum(1 for kw in kws if kw in text) for name, kws in rules.items()}
def _join(items: List[str]) -> str:
return ', '.join(items) if items else 'Not specified'
class SearchListingOptimizer:
def __init__(self, user_input: ListingInput):
self.raw = user_input
self.text = self._normalize_input(user_input)
self.lower = self.text.lower()
self.platform = self._detect_platform()
self.goal = self._detect_goal()
self.traps = self._detect_traps()
self.attributes = self._attribute_order()
def _normalize_input(self, user_input: ListingInput) -> str:
if isinstance(user_input, dict):
chunks: List[str] = []
for key in ['platform', 'product', 'listing', 'goal', 'notes']:
value = user_input.get(key)
if not value:
continue
if isinstance(value, list):
value = ', '.join(str(v) for v in value)
chunks.append(f'{key}: {value}')
return ' | '.join(chunks)
return str(user_input or '').strip()
def _detect_platform(self) -> str:
scores = _score_rules(self.lower, PLATFORM_RULES)
best = max(scores, key=scores.get)
return best if scores[best] > 0 else 'Amazon'
def _detect_goal(self) -> str:
if any(kw in self.lower for kw in ['new', 'launch', 'first', 'create', 'build']):
return 'New Listing'
if any(kw in self.lower for kw in ['convert', 'conversion', 'sales', 'order']):
return 'Conversion Improvement'
return 'Visibility Boost'
def _detect_traps(self) -> List[str]:
matched = [name for name, kws in CONVERSION_TRAPS.items() if any(kw in self.lower for kw in kws)]
defaults = ['Pricing', 'Description', 'Trust Signals']
return list(dict.fromkeys(matched + [d for d in defaults if d not in matched]))[:4]
def _attribute_order(self) -> List[str]:
return list(ATTRIBUTE_WEIGHT.keys())
def _scorecard_rows(self) -> List[Dict[str, str]]:
p = self.platform
rows = []
for attr in self.attributes:
weight = ATTRIBUTE_WEIGHT.get(attr, {}).get(p, ATTRIBUTE_WEIGHT.get(attr, {}).get('general', 10))
guidance = {
'Title': 'Front-load primary keyword; include brand, model, key benefit, and count/pack.',
'Bullets / Key Features': 'Lead bullet 1 with the biggest benefit; use remaining bullets for specs, use case, and social proof.',
'Description / Body': 'Open with the core benefit in one sentence; use the rest for proof points, scenarios, and trust.',
'Backend Keywords': 'Fill all available character slots; avoid duplicates with title or bullets.',
'Images': 'Main image on pure white background; add infographic, lifestyle, and comparison chart.',
'Reviews': 'Prioritize review velocity over star rating; address negative review themes proactively.',
'Price': 'Research the 3rd-party price comparison; price within 10% of the competitive range.',
}
rows.append({'attribute': attr, 'weight': f'{weight}%', 'guidance': guidance.get(attr, 'Optimize per platform guidelines.')})
return rows
def _keyword_angles(self) -> List[str]:
return [
'Identify the primary search intent: is it informational, navigational, or transactional?',
'Target the top 3 competitor ASIN titles for structural inspiration without copying.',
'Distribute primary keywords across Title, Bullet 1, and the first line of Description.',
'Use backend search terms for secondary keywords, misspellings, and synonyms.',
]
def _action_list(self) -> List[str]:
actions = [
'Rewrite the title with the primary keyword first, followed by a benefit and a spec.',
'Audit bullets: each bullet should independently convey a compelling reason to buy.',
'Add or refresh the main image; ensure it passes the platform main-image standard.',
'Build the description with a benefit-led opening, proof points, and a clear CTA.',
'Populate all backend search term fields with relevant secondary keywords and variants.',
]
if self.goal == 'New Listing':
actions.insert(0, 'Start with competitor research: find the top 3 ASINs and map their keyword structure.')
if self.goal == 'Conversion Improvement':
actions.insert(0, 'Run a conversion-trap audit: check pricing, trust signals, and review depth first.')
return actions[:6]
def render(self) -> str:
lines: List[str] = []
lines.append('# Search Listing Optimization Brief')
lines.append('')
lines.append(f'**Platform:** {self.platform}')
lines.append(f'**Optimization goal:** {self.goal}')
lines.append(f'**Conversion traps detected:** {_join(self.traps)}')
lines.append('**Method note:** This is a heuristic optimization brief. No live search analytics or platform API was accessed.')
lines.append('')
lines.append('## Platform Search Context')
platform_notes = {
'Amazon': 'A9 ranks by conversion rate,相关性, and customer satisfaction. Price and reviews also factor.',
'Taobao / 1688': 'Relevance + sales history + DSR. Titles should match buyer search phrasing.',
'JD': 'Similar to Amazon; logistics score (京东物流) also influences ranking.',
'TikTok Shop': 'Discovery is feed-driven; tags, titles, and engagement signals drive visibility.',
'Xiaohongshu': 'Keyword relevance + engagement + creator seeding;笔记 copy is critical.',
'Shopify': 'Search is storefront-driven; product metafields and tags influence search relevance.',
}
lines.append(f'- {platform_notes.get(self.platform, "Review platform-specific ranking factors before finalizing optimization.")}')
lines.append('')
lines.append('## Listing Attribute Scorecard')
lines.append('| Attribute | Est. Weight | Optimization Guidance |')
lines.append('|---|---|---|')
for row in self._scorecard_rows():
lines.append(f'| {row["attribute"]} | {row["weight"]} | {row["guidance"]} |')
lines.append('')
lines.append('## Keyword Strategy Angles')
for item in self._keyword_angles():
lines.append(f'- {item}')
lines.append('')
lines.append('## Conversion Trap Detection')
trap_notes = {
'Pricing': 'Compare against top 3 competitors and the platform median; price within 10–15% if possible.',
'Images': 'Require a compliant main image, at least 4 supplemental images, and 1 infographic.',
'Reviews': 'Aim for 10+ reviews with a 4.0+ average before expecting meaningful traffic.',
'Description': 'Lead with benefits, support with specs, and end with a low-friction CTA.',
'Trust Signals': 'Display return policy, warranty terms, and brand credibility above the fold.',
}
for trap in self.traps:
lines.append(f'### {trap}')
lines.append(f'- {trap_notes.get(trap, "Audit this area and address specific weaknesses.")}')
lines.append('')
lines.append('## Prioritized Action List')
for idx, action in enumerate(self._action_list(), 1):
lines.append(f'{idx}. {action}')
lines.append('')
lines.append('## Revised Title Template')
title_templates = {
'Amazon': '[Brand] [Product Name] [Key Feature 1] [Key Feature 2] [Count/Pack] — [Primary Benefit]',
'Taobao / 1688': '[品牌] [产品名称] [规格/型号] [核心卖点] [件数/套餐]',
'JD': '[品牌] [商品名称] [型号] [核心卖点] [件数]',
'TikTok Shop': '[Product Name] | [Key Benefit] | [Trending Keyword] #[Hashtag1] #[Hashtag2]',
'Xiaohongshu': '[产品名]测评 | [核心功效] | [适合人群] #[关键词1] #[关键词2]',
'Shopify': '[Product Name] — [Short Benefit Description] | [Brand]',
}
lines.append(f'```\n{title_templates.get(self.platform, "[Brand] [Product Name] [Key Feature] [Count]")}\n```')
return '\n'.join(lines)
def handle(user_input: ListingInput) -> str:
return SearchListingOptimizer(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import SearchListingOptimizer, handle
def test_platform_detection_amazon():
opt = SearchListingOptimizer('Amazon A9 listing audit for skincare product')
assert opt.platform == 'Amazon'
def test_platform_detection_xiaohongshu():
opt = SearchListingOptimizer('Xiaohongshu SEO audit for skincare brand')
assert opt.platform == 'Xiaohongshu'
def test_goal_detection_new_listing():
opt = SearchListingOptimizer('New skincare product listing on Amazon')
assert opt.goal == 'New Listing'
def test_goal_detection_conversion():
opt = SearchListingOptimizer('Improve conversion on our Amazon listing with high traffic but low sales')
assert opt.goal == 'Conversion Improvement'
def test_render_contains_sections():
output = handle('Audit our Amazon skincare listing for A9 visibility')
assert output.startswith('# Search Listing Optimization Brief')
assert '## Listing Attribute Scorecard' in output
assert '## Keyword Strategy Angles' in output
assert '## Prioritized Action List' in output
def test_dict_input_supported():
output = handle({
'platform': 'Amazon',
'product': 'skincare serum',
'goal': 'Visibility Boost',
'listing': 'Need title and bullet optimization'
})
assert '# Search Listing Optimization Brief' in output
assert 'Amazon' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Navigate Amazon Lightning Deal, Best Deal, Coupon, Shoppable Holiday; TikTok Shop Promo Zone; Shopify Discount Rules; Taobao Crowd-Collapse; JD 618; and othe...
---
name: promotion-enrollment-assistant
description: Navigate Amazon Lightning Deal, Best Deal, Coupon, Shoppable Holiday; TikTok Shop Promo Zone; Shopify Discount Rules; Taobao Crowd-Collapse; JD 618; and other platform promotion mechanisms. Use when operators need to compare promotion types, assess eligibility, plan enrollment timelines, or avoid common enrollment mistakes without live platform API access.
---
# Promotion Enrollment Assistant
## Overview
Use this skill to turn rough promotion intent into an operator-ready enrollment plan. It maps promotion types across major ecommerce platforms, checks eligibility heuristics, generates enrollment timelines, and surfaces common pitfalls before submission.
This MVP is heuristic. It does **not** access live seller portals, promotion APIs, or enrollment dashboards. It relies on the user's provided product context, platform, and promotion type.
## Trigger
Use this skill when the user wants to:
- compare promotion types across Amazon, TikTok Shop, JD, Taobao, or Shopify
- assess whether a SKU or category is likely eligible for a specific deal type
- plan an enrollment timeline with submission windows and approval lead times
- avoid common rejection reasons for Lightning Deal, Coupon, Best Deal, or similar mechanisms
- prepare a promotion enrollment checklist for a specific platform and campaign window
### Example prompts
- "Help me enroll in Amazon Lightning Deal for our skincare SKU this Prime Day"
- "What promotion types should we use on TikTok Shop for a mid-season flash sale?"
- "Create an enrollment timeline for our JD 618 campaign"
- "How do we avoid Amazon promotion rejection for our Home category?"
## Workflow
1. Capture the platform, promotion type, target SKU or category, and campaign window.
2. Map eligibility heuristics for the specific promotion type.
3. Build the enrollment checklist and timeline.
4. Surface common rejection patterns and how to avoid them.
5. Return a markdown enrollment brief.
## Inputs
The user can provide any mix of:
- platform: Amazon, TikTok Shop, JD, Taobao, Shopify, Xiaohongshu, or general
- promotion type: Lightning Deal, Best Deal, Coupon, Crowd-Collapse, Flash Sale, Bundle Deal, 618, 11.11, Prime Day, Black Friday
- SKU or category context: product type, price range, rating, fulfillment method
- campaign window: specific dates, seasonal event, or general timing
- historical context: prior enrollment rejections, approval rates, or deal performance
## Outputs
Return a markdown brief with:
- promotion type summary and how it works
- eligibility assessment for the given SKU or category
- enrollment timeline (submission deadline → review → approval → go-live)
- checklist: required materials, pricing constraints, inventory requirements
- common rejection reasons and mitigation steps
- platform-specific notes and tips
## Safety
- No live seller portal or enrollment API access.
- Eligibility assessment is directional; final approval depends on platform review.
- Do not claim guaranteed enrollment or specific deal placement.
- Pricing and margin decisions remain with the operator.
## Best-fit Scenarios
- sellers enrolling in platform deals for the first time or expanding to new platforms
- operators managing multiple promotion types across channels
- brands preparing for major shopping festivals (618, 11.11, Prime Day, Black Friday)
## Not Ideal For
- real-time enrollment status tracking or automated submission
- enterprise-grade promotion management with complex legal or compliance requirements
- platforms or deal types not covered in the built-in playbook library
## Acceptance Criteria
- Return markdown text.
- Include eligibility assessment, enrollment timeline, and rejection-mitigation steps.
- Cover at least one specific promotion type with actionable checklist items.
- Make eligibility assumptions explicit.
- Keep the brief practical for platform sellers and ecommerce operators.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import Any, Dict, List, Union
PLATFORM_RULES = {
'Amazon': ['amazon', 'seller central', 'vendor central'],
'TikTok Shop': ['tiktok', 'tik tok', 'douyin'],
'JD': ['jd', 'jingdong', 'jd.com'],
'Taobao': ['taobao', '1688'],
'Shopify': ['shopify', 'storefront'],
'Xiaohongshu': ['xiaohongshu', 'rednote', 'xhs'],
'General': ['general', 'all platform', 'multi-platform'],
}
PROMO_RULES = {
'Lightning Deal': {
'platform': 'Amazon',
'duration': '4–6 hours',
'discount': '15–50% off',
'eligibility': 'Professional seller, 3+ reviews, inventory depth, category eligibility',
'submission': '3–4 weeks before event',
'rejection_reasons': ['Insufficient review count', 'Inventory too thin', 'Category not eligible', 'Pricing below floor'],
},
'Best Deal': {
'platform': 'Amazon',
'duration': '1–2 weeks',
'discount': '10–30% off',
'eligibility': 'Professional seller, 1+ review, Buy Box eligibility',
'submission': '2–3 weeks before event',
'rejection_reasons': ['Buy Box lost', 'Account health below threshold', 'Inventory inconsistency'],
},
'Coupon': {
'platform': 'Amazon',
'duration': '1–90 days',
'discount': '5–50% off',
'eligibility': 'Any seller with inventory',
'submission': '1–2 weeks before launch',
'rejection_reasons': ['Overlapping with other promotions', 'Pricing below 50% of recent price'],
},
'Prime Day': {
'platform': 'Amazon',
'duration': '48 hours',
'discount': '20–40% off recommended',
'eligibility': 'Prime-eligible, strong review profile, inventory for 2x expected volume',
'submission': 'Invitations sent 6–8 weeks before; by-invitation only',
'rejection_reasons': ['No invitation', 'Account health issue', 'Inventory risk'],
},
'TikTok Shop Promo Zone': {
'platform': 'TikTok Shop',
'duration': '4–24 hours',
'discount': '10–30% off + creator佣金补贴',
'eligibility': 'Store rating 4.0+, live-f直播资格, sufficient inventory',
'submission': '1–2 weeks before via seller center',
'rejection_reasons': ['Low store rating', 'Insufficient inventory', 'Category restriction', 'Price too low'],
},
'JD 618': {
'platform': 'JD',
'duration': 'Mid-June (typically June 1–18)',
'discount': 'Platform-defined, typically 15–30% off',
'eligibility': 'JD store, category participation, inventory commitment, margin deposit',
'submission': 'April–May (pre-registration)',
'rejection_reasons': ['Margin deposit insufficient', 'Category not in event', 'Inventory plan rejected'],
},
'Taobao Crowd-Collapse (聚划算)': {
'platform': 'Taobao',
'duration': '24–72 hours',
'discount': '20–50% off',
'eligibility': 'Gold seller status, 4.8+ DSR, category eligibility',
'submission': '3–5 days before via Taobao Seller Center',
'rejection_reasons': ['DSR below threshold', 'Low seller rank', 'Category not eligible'],
},
'11.11 / Black Friday': {
'platform': 'Multi',
'duration': 'Nov 11 (CN) / Black Fri–Cyber Mon (Western)',
'discount': 'Platform-specific, typically 20–50% off',
'eligibility': 'Varies by platform; generally high review count, inventory depth, account health',
'submission': '6–8 weeks before',
'rejection_reasons': ['Account health', 'Inventory commitment', 'Margin constraints'],
},
}
EnrollmentInput = Union[str, Dict[str, Any]]
def _score_rules(text: str, rules: Dict[str, List[str]]) -> Dict[str, int]:
return {name: sum(1 for kw in kws if kw in text) for name, kws in rules.items()}
def _join(items: List[str]) -> str:
return ', '.join(items) if items else 'Not specified'
class PromotionEnrollmentAssistant:
def __init__(self, user_input: EnrollmentInput):
self.raw = user_input
self.text = self._normalize_input(user_input)
self.lower = self.text.lower()
self.platform = self._detect_platform()
self.promo_type = self._detect_promo_type()
self.promo_info = PROMO_RULES.get(self.promo_type, None)
def _normalize_input(self, user_input: EnrollmentInput) -> str:
if isinstance(user_input, dict):
chunks: List[str] = []
for key in ['platform', 'promo_type', 'sku', 'category', 'window', 'notes']:
value = user_input.get(key)
if not value:
continue
if isinstance(value, list):
value = ', '.join(str(v) for v in value)
chunks.append(f'{key}: {value}')
return ' | '.join(chunks)
return str(user_input or '').strip()
def _detect_platform(self) -> str:
scores = _score_rules(self.lower, PLATFORM_RULES)
best = max(scores, key=scores.get)
return best if scores[best] > 0 else 'Amazon'
def _detect_promo_type(self) -> str:
known_promos = list(PROMO_RULES.keys())
matched = [p for p in known_promos if p.lower().replace(' ', '') in self.lower.replace(' ', '')]
if matched:
return matched[0]
for name in ['Lightning Deal', 'Best Deal', 'Coupon', 'TikTok Shop Promo Zone', 'JD 618', 'Taobao Crowd-Collapse (聚划算)', '11.11 / Black Friday', 'Prime Day']:
if name.lower().split(' ')[0] in self.lower:
return name
return 'Lightning Deal'
def _eligibility_checklist(self) -> List[str]:
if not self.promo_info:
return ['- Promotion type not recognized; confirm eligibility directly with platform seller support.']
info = self.promo_info
return [
f'- **Platform:** {info["platform"]}',
f'- **Duration:** {info["duration"]}',
f'- **Typical discount:** {info["discount"]}',
f'- **Eligibility:** {info["eligibility"]}',
]
def _rejection_mitigation(self) -> List[str]:
if not self.promo_info:
return []
return [f'- **{r}** — mitigation: review inventory, pricing history, and account health metrics before resubmission.'
for r in self.promo_info['rejection_reasons']]
def _timeline(self) -> List[str]:
if not self.promo_info:
return ['- Submit 2–4 weeks before the target deal window.']
submission = self.promo_info.get('submission', '2–4 weeks before')
return [
f'- **Pre-registration:** {submission}',
'- **Submission:** Via platform seller center (not third-party tools)',
'- **Review period:** 3–10 business days depending on platform',
'- **Approval / Rejection notice:** 1 week before deal goes live',
'- **Inventory lock:** Typically required 48–72 hours before deal starts',
]
def render(self) -> str:
lines: List[str] = []
lines.append('# Promotion Enrollment Brief')
lines.append('')
lines.append(f'**Platform:** {self.platform}')
lines.append(f'**Promotion type:** {self.promo_type}')
lines.append('**Method note:** This is a heuristic enrollment brief. No live seller portal or promotion API was accessed.')
lines.append('')
if self.promo_info:
lines.append('## How It Works')
lines.append(f'- **Platform:** {self.promo_info["platform"]}')
lines.append(f'- **Duration:** {self.promo_info["duration"]}')
lines.append(f'- **Typical discount range:** {self.promo_info["discount"]}')
lines.append('')
lines.append('## Eligibility Assessment')
for item in self._eligibility_checklist():
lines.append(item)
lines.append('')
lines.append('## Enrollment Timeline')
for item in self._timeline():
lines.append(item)
lines.append('')
lines.append('## Required Checklist')
checks = [
'Confirm product category eligibility on the target platform',
'Verify review count or rating meets platform minimum',
'Check inventory depth: plan for 2–3x expected deal volume',
'Confirm pricing floor and discount margin will protect gross margin',
'Review account health metrics (ODR, late shipment rate) 30 days before submission',
'Prepare deal-related creative assets (badges, ad copy) in advance',
]
for check in checks:
lines.append(f'- [ ] {check}')
lines.append('')
lines.append('## Common Rejection Reasons & Mitigation')
for item in self._rejection_mitigation():
lines.append(item)
lines.append('')
lines.append('## Platform-Specific Tips')
tips = {
'Amazon': [
'- Lightning Deal submission requires a referral fee for each deal submission.',
'- Best Deal can run alongside Lightning Deal if the discount differential is justified.',
'- Prime Day is by invitation only; build the relationship through consistent deal performance.',
],
'TikTok Shop': [
'- TikTok creator佣金补贴 can amplify deal economics — coordinate with at least 2–3 creators pre-deal.',
'- Live streaming during the promo zone significantly lifts conversion.',
'- Ensure your store rating is 4.0+ before applying.',
],
'JD': [
'- JD requires a margin deposit upfront for 618 participation — plan cash flow accordingly.',
'- Coordinate JD logistics (入仓) timing carefully; late warehouse entry disqualifies the deal.',
],
'Taobao': [
'- Gold seller status (金冠) is a hard prerequisite for 聚划算.',
'- DSR decline in the 30 days before application can trigger automatic rejection.',
],
'General': [
'- Always cross-check submission deadlines against the official platform event calendar.',
'- Maintain a buffer between your discount and the platform floor.',
],
}
platform_tips = tips.get(self.platform, tips['General'])
for tip in platform_tips:
lines.append(f'- {tip}')
return '\n'.join(lines)
def handle(user_input: EnrollmentInput) -> str:
return PromotionEnrollmentAssistant(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import PromotionEnrollmentAssistant, handle
def test_platform_detection_amazon():
assistant = PromotionEnrollmentAssistant('Amazon Lightning Deal enrollment for Prime Day')
assert assistant.platform == 'Amazon'
def test_platform_detection_tiktok():
assistant = PromotionEnrollmentAssistant('TikTok Shop flash sale enrollment')
assert assistant.platform == 'TikTok Shop'
def test_promo_type_detection_lightning_deal():
assistant = PromotionEnrollmentAssistant('Amazon Lightning Deal')
assert assistant.promo_type == 'Lightning Deal'
def test_promo_type_detection_jd_618():
assistant = PromotionEnrollmentAssistant('JD 618 promotion enrollment')
assert assistant.promo_type == 'JD 618'
def test_render_contains_sections():
output = handle('Help me enroll in Amazon Lightning Deal for our skincare SKU this Prime Day')
assert output.startswith('# Promotion Enrollment Brief')
assert '## How It Works' in output
assert '## Enrollment Timeline' in output
assert '## Common Rejection Reasons' in output
def test_dict_input_supported():
output = handle({
'platform': 'Amazon',
'promo_type': 'Lightning Deal',
'sku': 'skincare-serum-30ml',
'window': 'Prime Day'
})
assert '# Promotion Enrollment Brief' in output
assert 'Lightning Deal' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Plan ecommerce campaign calendars across Shopify, Amazon, TikTok Shop, Xiaohongshu, and other channels using built-in seasonal frameworks, promotion playbook...
---
name: campaign-calendar-planner
description: Plan ecommerce campaign calendars across Shopify, Amazon, TikTok Shop, Xiaohongshu, and other channels using built-in seasonal frameworks, promotion playbooks, and channel-timing templates. Use when preparing quarterly campaign plans, monthly promotion schedules, holiday retail calendars, or multi-channel campaign sequencing without live project-management or ad-platform integrations.
---
# Campaign Calendar Planner
## Overview
Use this skill to turn rough campaign ideas, seasonal goals, and channel priorities into an operator-ready campaign calendar. It applies a built-in seasonal framework, promotion playbook library, and channel-timing matrix to generate a structured markdown brief.
This MVP is heuristic. It does **not** connect to live ad platforms, calendars, or ERP systems. It relies on the user's provided campaign context, seasonal intent, and channel mix.
## Trigger
Use this skill when the user wants to:
- build a quarterly or monthly campaign calendar from scratch
- map a holiday or seasonal promotion across multiple channels
- sequence campaign types (awareness → conversion → retention) over a time window
- audit whether campaign timing, frequency, and channel mix are aligned
- turn rough campaign notes into an organized execution calendar
### Example prompts
- "Create a Q2 campaign calendar for our Shopify store"
- "Build a Mother's Day promotion plan across TikTok Shop and Xiaohongshu"
- "Help me plan a summer flash-sale series across Amazon and our DTC store"
- "Map our channel campaign sequencing for the next 90 days"
## Workflow
1. Capture the planning window, primary campaign type, and target channels.
2. Apply the seasonal framework to identify high-signal periods and key dates.
3. Map campaign types to channels and sequence them across the window.
4. Generate a channel-timing matrix and priority calendar.
5. Return a markdown brief with calendar grid, campaign cards, and execution notes.
## Inputs
The user can provide any mix of:
- planning window: Q1 / Q2 / Q3 / Q4, half-year, full year, or specific date range
- campaign goal: awareness, traffic, conversion, revenue, retention, or brand
- channels: Shopify, Amazon, TikTok Shop, Xiaohongshu, WeChat, email, Meta Ads, Google Ads
- seasonal context: holiday name, shopping festival, or rough seasonal theme
- budget tone: aggressive, steady, experimental
- product focus: new launch, clearance, core SKUs, premium push
## Outputs
Return a markdown brief with:
- planning window and campaign thesis
- seasonal framework and key-date map
- channel-timing matrix (channel × week/month)
- campaign card for each major promotion (name, type, channels, timing, goal)
- frequency and cadence recommendations
- execution notes and common pitfalls
## Safety
- No live calendar, ad platform, or ERP access.
- Seasonal-date references are directional; users should verify official festival and sale dates.
- Campaign budgets and media spend recommendations are advisory.
- All channel activation decisions remain human-approved.
## Best-fit Scenarios
- SMB and mid-market ecommerce teams planning quarterly or seasonal campaigns
- brands running multi-channel promotions without a dedicated campaign manager
- teams that need a reusable planning framework instead of scattered notes
## Not Ideal For
- real-time campaign management, live ad spend control, or automated execution
- very small experimental runs with no defined planning window
- regulated categories requiring pre-approval for promotional claims
## Acceptance Criteria
- Return markdown text.
- Include calendar grid, campaign cards, and execution notes.
- Cover at least 3 key seasonal dates within the planning window.
- Make timing assumptions explicit.
- Keep the brief practical for ecommerce operators and channel managers.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import Any, Dict, List, Union
WINDOW_RULES = {
'Q1': ['q1', 'q1', 'january', 'february', 'march', 'new year', 'spring'],
'Q2': ['q2', 'april', 'may', 'june', 'spring', 'easter', "mother's day"],
'Q3': ['q3', 'july', 'august', 'september', 'summer', 'back to school'],
'Q4': ['q4', 'october', 'november', 'december', 'holiday', 'black friday', 'christmas', '11.11'],
}
GOAL_RULES = {
'Awareness': ['awareness', 'brand', 'reach', 'impression', 'brand campaign'],
'Traffic': ['traffic', 'click', 'visit', 'landing page', 'ctr'],
'Conversion': ['conversion', 'sales', 'orders', 'revenue', 'direct response'],
'Revenue': ['revenue', 'gmv', 'sales', 'order value', 'aov'],
'Retention': ['retention', 'loyalty', 'repeat', 'vip', 'lapsed', 'winback'],
'New Launch': ['launch', 'new product', 'new sku', 'rollout', 'go live'],
}
CHANNEL_RULES = {
'Shopify / DTC': ['shopify', 'dtc', 'storefront', 'pdp'],
'Amazon': ['amazon'],
'TikTok Shop': ['tiktok', 'douyin'],
'Xiaohongshu': ['xiaohongshu', 'rednote', 'xhs'],
'Meta Ads': ['meta', 'facebook', 'instagram', 'paid social'],
'Google Ads': ['google', 'youtube'],
'Email / CRM': ['email', 'crm', 'klaviyo', 'mailchimp'],
'JD': ['jd', 'jingdong'],
'Taobao': ['taobao', '1688'],
'WeChat': ['wechat', 'weixin'],
}
SEASONAL_DATES = {
"New Year's Day": "January 1",
"Lunar New Year": "Late January – February",
"Valentine's Day": "February 14",
"International Women's Day": "March 8",
"Easter": "March – April",
"Q1 Sales Season": "January – March",
"April Sale / Spring": "April",
"May Day": "May 1",
"Mother's Day (CN)": "Second Sunday of May",
"618 Mid-Year Festival": "June 18",
"Summer Sale": "June – August",
"Back to School": "August – September",
"Mid-Autumn Festival": "September – October",
"National Day (CN)": "October 1",
"11.11 Singles Day": "November 11",
"Black Friday / Cyber Monday": "Fourth week of November",
"Holiday Season": "December",
"Christmas / Year End": "December 25",
}
CampaignInput = Union[str, Dict[str, Any]]
def _score_rules(text: str, rules: Dict[str, List[str]]) -> Dict[str, int]:
return {name: sum(1 for kw in kws if kw in text) for name, kws in rules.items()}
def _join(items: List[str]) -> str:
return ', '.join(items) if items else 'Not specified'
class CampaignCalendarPlanner:
def __init__(self, user_input: CampaignInput):
self.raw = user_input
self.text = self._normalize_input(user_input)
self.lower = self.text.lower()
self.window = self._detect_window()
self.goal = self._detect_goal()
self.channels = self._detect_channels()
self.key_dates = self._select_key_dates()
def _normalize_input(self, user_input: CampaignInput) -> str:
if isinstance(user_input, dict):
chunks: List[str] = []
for key in ['window', 'goal', 'channels', 'seasonal', 'budget', 'product', 'notes']:
value = user_input.get(key)
if not value:
continue
if isinstance(value, list):
value = ', '.join(str(v) for v in value)
chunks.append(f'{key}: {value}')
return ' | '.join(chunks)
return str(user_input or '').strip()
def _detect_window(self) -> str:
scores = _score_rules(self.lower, WINDOW_RULES)
best = max(scores, key=scores.get)
return best if scores[best] > 0 else 'Q2'
def _detect_goal(self) -> str:
scores = _score_rules(self.lower, GOAL_RULES)
best = max(scores, key=scores.get)
return best if scores[best] > 0 else 'Conversion'
def _detect_channels(self) -> List[str]:
matched = [name for name, kws in CHANNEL_RULES.items() if any(kw in self.lower for kw in kws)]
return matched or ['Shopify / DTC', 'Email / CRM']
def _select_key_dates(self) -> List[str]:
window_map = {
'Q1': ["New Year's Day", "Lunar New Year", "Valentine's Day", "Q1 Sales Season"],
'Q2': ["April Sale / Spring", "May Day", "Mother's Day (CN)", "618 Mid-Year Festival"],
'Q3': ["Summer Sale", "Back to School", "Mid-Autumn Festival", "National Day (CN)"],
'Q4': ["11.11 Singles Day", "Black Friday / Cyber Monday", "Holiday Season", "Christmas / Year End"],
}
return window_map.get(self.window, list(SEASONAL_DATES.keys()))
def _channel_timing_matrix(self) -> List[str]:
lines = ['| Channel | Focus | Timing | Campaign Type |']
lines.append('|---|---|---|---|')
for ch in self.channels:
timing = {
'Shopify / DTC': 'Always-on + campaign peaks',
'Amazon': 'Deal events + search campaigns',
'TikTok Shop': 'Live streams + content spikes',
'Xiaohongshu': 'Seeding 2–4 weeks pre-launch',
'Meta Ads': 'Traffic + retargeting around key dates',
'Google Ads': 'Intent-based around shopping festivals',
'Email / CRM': 'Segmentation sequences around campaigns',
'JD': 'Platform deal events (618, 11.11)',
'Taobao': 'Live streaming + deal events',
'WeChat': ' MOM and festival content pushes',
}.get(ch, 'Campaign-driven')
ctype = {
'Awareness': 'Brand + video',
'Traffic': 'Content + CTR',
'Conversion': 'Direct response + deals',
'Revenue': 'Bundles + upsell',
'Retention': 'Email sequences + VIP',
'New Launch': 'Launch-day push + UGC',
}.get(self.goal, 'Mixed')
lines.append(f'| {ch} | Always-on + peaks | {timing} | {ctype} |')
return lines
def _campaign_cards(self) -> List[str]:
dates = self.key_dates[:3]
cards = []
for i, date in enumerate(dates, 1):
cards.append(f'{i}. **{date}** — Theme: TBD | Channels: {_join(self.channels)} | Goal: {self.goal}')
if self.goal == 'New Launch':
cards.insert(0, f'1. **Launch Day** — Theme: New SKU Introduction | Channels: {_join(self.channels[:3])} | Goal: {self.goal}')
elif self.goal == 'Retention':
cards.insert(0, f'1. **VIP Loyalty Push** — Theme: Thank You + Exclusive | Channels: Email / CRM, WeChat | Goal: Retention')
return cards
def _cadence_notes(self) -> List[str]:
return [
'Space major campaigns 4–6 weeks apart to avoid audience fatigue and preserve margin.',
'Use always-on retargeting at 20–30% of budget to sustain baseline conversion between campaign peaks.',
'Front-load awareness and seeding (TikTok, Xiaohongshu) 2–3 weeks before conversion push.',
'Align paid search and Amazon deal enrollment with content momentum, not in isolation.',
]
def _execution_notes(self) -> List[str]:
return [
'Verify official festival and sale dates on each platform — they shift between China and Western calendars.',
'Lock channel budgets and creative briefs at least 3 weeks before the campaign window opens.',
'Set up weekly campaign performance check-ins starting 1 week before launch.',
'Prepare a campaign retrospective template before launch so post-mortem is not an afterthought.',
]
def render(self) -> str:
lines: List[str] = []
lines.append('# Campaign Calendar Planner')
lines.append('')
lines.append(f'**Planning window:** {self.window}')
lines.append(f'**Primary goal:** {self.goal}')
lines.append(f'**Channels in scope:** {_join(self.channels)}')
lines.append('**Method note:** This is a heuristic planning brief. No live calendar, ad platform, or ERP system was accessed.')
lines.append('')
lines.append('## Campaign Thesis')
lines.append(f'- Plan the {self.window} campaign calendar around {_join(self.key_dates)} as anchor dates.')
lines.append(f'- Lead with a {self.goal.lower()} focus and sequence channel activations to support that goal.')
lines.append('- Treat awareness channels as predecessors to conversion channels; do not activate everything at once.')
lines.append('')
lines.append('## Seasonal Key Dates')
for date, period in SEASONAL_DATES.items():
marker = ' ← anchor' if date in self.key_dates else ''
lines.append(f'- **{date}**: {period}{marker}')
lines.append('')
lines.append('## Channel-Timing Matrix')
for row in self._channel_timing_matrix():
lines.append(row)
lines.append('')
lines.append('## Campaign Cards')
for card in self._campaign_cards():
lines.append(f'- {card}')
lines.append('')
lines.append('## Cadence Recommendations')
for note in self._cadence_notes():
lines.append(f'- {note}')
lines.append('')
lines.append('## Execution Notes')
for note in self._execution_notes():
lines.append(f'- {note}')
return '\n'.join(lines)
def handle(user_input: CampaignInput) -> str:
return CampaignCalendarPlanner(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import CampaignCalendarPlanner, handle
def test_window_detection_q1():
planner = CampaignCalendarPlanner('Build a Q1 campaign calendar across Shopify and Amazon')
assert planner.window == 'Q1'
assert "New Year's Day" in planner.key_dates or "Lunar New Year" in planner.key_dates
def test_window_detection_q4():
planner = CampaignCalendarPlanner('Plan our holiday campaign for Q4')
assert planner.window == 'Q4'
assert "11.11 Singles Day" in planner.key_dates or "Black Friday" in planner.key_dates
def test_goal_detection_conversion():
planner = CampaignCalendarPlanner('Q2 campaign focused on direct sales and revenue')
assert planner.goal == 'Conversion'
def test_channel_detection_amazon_tiktok():
planner = CampaignCalendarPlanner('Plan our Amazon Prime Day and TikTok Shop promotion')
assert 'Amazon' in planner.channels
assert 'TikTok Shop' in planner.channels
def test_render_contains_sections():
output = handle('Create a Q2 campaign calendar for our Shopify store targeting conversion')
assert output.startswith('# Campaign Calendar Planner')
assert '## Seasonal Key Dates' in output
assert '## Channel-Timing Matrix' in output
assert '## Campaign Cards' in output
def test_dict_input_supported():
output = handle({
'window': 'Q4',
'goal': 'Revenue',
'channels': ['Shopify', 'Amazon', 'TikTok Shop'],
'notes': 'Focus on holiday season'
})
assert '# Campaign Calendar Planner' in output
assert 'Q4' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Scan ecommerce inventory notes or CSV exports for stockout, overstock, aging, inbound-delay, and promo-readiness risk, then turn them into a ranked watchlist...
---
name: inventory-risk-radar
description: Scan ecommerce inventory notes or CSV exports for stockout, overstock, aging, inbound-delay, and promo-readiness risk, then turn them into a ranked watchlist, days-of-cover summary, action ladder, scenario notes, and operator-ready meeting brief. Use when inventory planners, operations leads, finance-aware operators, founders, or marketplace teams need a lightweight risk-detection layer without live ERP, WMS, or demand-forecasting integrations.
---
# Inventory Risk Radar
## Overview
Use this skill to turn inventory, sales, and supplier notes into a practical risk brief. It helps teams prioritize which SKUs need immediate attention, what kind of risk is present, and which action path should be reviewed first.
This MVP is heuristic. It does **not** connect to live ERP, WMS, purchase-order systems, or forecasting engines. It relies on the user's provided stock position, velocity, lead-time, and business-context notes.
## Trigger
Use this skill when the user wants to:
- flag stockout, overstock, aging, or inbound-delay risk
- prepare a daily, weekly, or pre-promo inventory watchlist
- estimate where cash or revenue risk is likely building
- turn rough inventory exports into an ops-ready action memo
- align replenishment, markdown, and supplier follow-up priorities
### Example prompts
- "Create a daily inventory risk watchlist from these notes"
- "Which SKUs look most exposed before our promotion?"
- "Help me prioritize stockout versus overstock risk"
- "Turn this inventory and lead-time export into an ops meeting brief"
## Workflow
1. Capture the review mode, such as daily watchlist, replenishment review, pre-promo scan, or cash-risk review.
2. Normalize the likely inventory signals: on-hand, inbound, sales velocity, lead time, and aging pressure.
3. Classify the major risk types and translate them into business exposure.
4. Build the watchlist, action ladder, and scenario notes.
5. Return a markdown brief that operators can use in review meetings.
## Inputs
The user can provide any mix of:
- on-hand, reserved, available, inbound, or backorder notes
- recent sales velocity, sell-through, or promo assumptions
- supplier lead time, PO status, inbound ETA, or vendor reliability notes
- aging days, markdown history, or storage constraints
- margin, cash sensitivity, or strategic-SKU context
- review purpose such as daily monitoring, replenishment planning, or promo readiness
## Outputs
Return a markdown risk brief with:
- inventory risk dashboard
- days-of-cover summary
- critical, warning, and watch-tier notes
- action ladder and scenario notes
- owner-ready watchlist guidance
- assumptions, confidence notes, and limits
## Safety
- Do not claim access to live ERP, WMS, or supplier systems.
- Treat exposure estimates as directional unless the user provides complete inputs.
- Do not auto-create POs, transfers, markdowns, or ad changes.
- Keep replenishment, liquidation, and promo decisions human-approved.
- Reduce confidence when lead time, velocity, or SKU mapping is incomplete.
## Best-fit Scenarios
- SMB and mid-market ecommerce teams managing recurring stock risk
- weekly replenishment meetings or pre-promo reviews
- brands balancing stockout loss against cash tied up in slow movers
- operators who need a lighter decision layer than a full planning suite
## Not Ideal For
- manufacturing-grade MRP planning with BOM and production scheduling
- highly automated warehouse orchestration or real-time fulfillment control
- businesses with almost no sales, stock, or lead-time history
- workflows that require automatic purchasing or system execution
## Example Output Pattern
A strong response should:
- separate stockout, overstock, aging, and inbound-delay logic clearly
- show severity and likely action path, not only raw inventory commentary
- connect revenue risk and cash risk in the same brief when relevant
- include scenario thinking for promo uplift or inbound slippage
- surface assumptions when data quality is weak
## Acceptance Criteria
- Return markdown text.
- Include dashboard, watchlist, action, and limits sections.
- Make advisory limits explicit.
- Keep the brief practical for operations, purchasing, and finance-aware operators.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import Any, Dict, List, Union
REVIEW_MODE_RULES = {
'Daily Watchlist': ['daily', 'today', 'watchlist', 'monitor', 'health check'],
'Replenishment Planning': ['replenishment', 'reorder', 'restock', 'days of cover', 'cover', 'buy plan'],
'Pre-Promo Risk Review': ['promo', 'promotion', 'campaign', 'holiday', 'launch', 'event'],
'Cash-Risk Review': ['cash', 'aging', 'overstock', 'markdown', 'month-end', 'cashflow'],
}
RISK_RULES = {
'Stockout Risk': {
'keywords': ['stockout', 'oos', 'out of stock', 'run out', 'low stock', 'cover'],
'why': 'The best sellers can lose demand quickly when available stock falls below realistic lead-time protection.',
'check': 'Review days of cover, top-SKU share, and whether inbound timing really protects the promo window.',
'action': 'Reorder, expedite, or reduce exposure before demand turns into lost revenue.',
},
'Overstock Risk': {
'keywords': ['overstock', 'excess', 'slow moving', 'storage', 'too much stock'],
'why': 'Too much stock can trap cash, reduce flexibility, and force discounting later.',
'check': 'Compare stock depth against recent velocity, margin quality, and storage pressure.',
'action': 'Slow buying, rebalance channels, or build a controlled sell-down plan.',
},
'Aging Inventory Risk': {
'keywords': ['aging', 'aged', 'dead stock', 'markdown', 'expiry', 'expiring'],
'why': 'Older inventory often turns into cash drag, quality risk, or forced markdown exposure.',
'check': 'Review aging buckets, repeat markdown history, and which SKUs no longer have a healthy sell-through story.',
'action': 'Bundle, liquidate, or deliberately deprioritize future buys for the affected SKUs.',
},
'Inbound Delay Risk': {
'keywords': ['inbound', 'eta', 'delay', 'lead time', 'supplier', 'purchase order', 'po'],
'why': 'A stable stock position can become fragile quickly when supplier timing is unreliable.',
'check': 'Inspect vendor reliability, ETA confidence, and whether alternative supply exists.',
'action': 'Escalate supplier follow-up, pull forward reorders, or line up backup supply options.',
},
'Promo Readiness Risk': {
'keywords': ['promo', 'promotion', 'campaign', 'launch', 'event', 'peak'],
'why': 'Promotions magnify existing inventory weaknesses and can turn a manageable issue into a visible miss.',
'check': 'Stress test hero SKUs, substitute options, and channel allocation before spend increases.',
'action': 'Adjust promo intensity, shift spend, or protect inventory for the highest-value channels.',
},
}
SIGNAL_RULES = {
'On-hand / Available Stock': ['on-hand', 'on hand', 'available', 'reserved', 'stock'],
'Inbound / PO Status': ['inbound', 'po', 'purchase order', 'eta', 'backorder'],
'Sales Velocity': ['velocity', 'sales', 'sell-through', 'sell through', 'demand'],
'Lead Time / Supplier Reliability': ['lead time', 'supplier', 'vendor', 'factory'],
'Margin / Cash Pressure': ['margin', 'cash', 'storage', 'markdown'],
}
RadarInput = Union[str, Dict[str, Any]]
def _score_rules(text: str, rules: Dict[str, List[str]]) -> Dict[str, int]:
return {name: sum(1 for keyword in keywords if keyword in text) for name, keywords in rules.items()}
def _join(items: List[str]) -> str:
return ', '.join(items) if items else 'None explicitly provided'
class InventoryRiskRadar:
def __init__(self, user_input: RadarInput):
self.raw = user_input
self.text = self._normalize_input(user_input)
self.lower = self.text.lower()
self.review_mode = self._detect_review_mode()
self.risk_types = self._detect_risk_types()
self.signals = self._detect_signals()
def _normalize_input(self, user_input: RadarInput) -> str:
if isinstance(user_input, dict):
chunks: List[str] = []
for key in ['review_mode', 'inventory', 'demand', 'supply', 'constraints', 'notes']:
value = user_input.get(key)
if not value:
continue
if isinstance(value, list):
value = ', '.join(str(item) for item in value)
chunks.append(f'{key}: {value}')
return ' | '.join(chunks)
return str(user_input or '').strip()
def _detect_review_mode(self) -> str:
scores = _score_rules(self.lower, REVIEW_MODE_RULES)
best = max(scores, key=scores.get)
return best if scores[best] > 0 else 'Daily Watchlist'
def _detect_risk_types(self) -> List[str]:
matched = [name for name, data in RISK_RULES.items() if any(keyword in self.lower for keyword in data['keywords'])]
ordered = []
for name in ['Stockout Risk', 'Inbound Delay Risk', 'Promo Readiness Risk', 'Overstock Risk', 'Aging Inventory Risk']:
if name in matched and name not in ordered:
ordered.append(name)
defaults = ['Stockout Risk', 'Overstock Risk', 'Inbound Delay Risk']
for name in defaults:
if name not in ordered:
ordered.append(name)
return ordered[:4]
def _detect_signals(self) -> List[str]:
matched = [name for name, keywords in SIGNAL_RULES.items() if any(keyword in self.lower for keyword in keywords)]
return matched or ['On-hand / Available Stock', 'Sales Velocity', 'Lead Time / Supplier Reliability']
def _dashboard_rows(self) -> List[Dict[str, str]]:
rows = []
for risk in self.risk_types:
info = RISK_RULES[risk]
rows.append({
'risk': risk,
'why': info['why'],
'check': info['check'],
'action': info['action'],
})
return rows
def _coverage_notes(self) -> List[str]:
notes = [
'Separate hero SKUs from the rest of the catalog before discussing average days of cover.',
'Treat inbound stock as protected only when ETA confidence is high enough to matter operationally.',
'Review whether the team is using one consistent definition for available, reserved, inbound, and backorder stock.',
]
if self.review_mode == 'Pre-Promo Risk Review':
notes.insert(0, 'Stress test cover using a promo uplift assumption rather than recent average demand alone.')
return notes[:4]
def _watchlist(self) -> List[str]:
items = [
'Critical: SKUs with thin cover, high strategic importance, or unreliable inbound protection.',
'Warning: SKUs with acceptable current stock but visible supplier, lead-time, or promo exposure.',
'Watch: Slow movers, aged stock, or channel-specific inventory pockets that can become cash drag.',
'Owner note: assign one owner for demand, one for supply, and one for promo or markdown decisions when risk crosses teams.',
]
return items
def _scenario_notes(self) -> List[str]:
notes = [
'If demand spikes, review whether the top sellers have substitutes that can absorb overflow without hurting margin too badly.',
'If inbound slips by 7 to 14 days, identify which SKUs move from manageable risk to immediate action territory.',
'If the promo goes live as planned, protect inventory for the channels with the cleanest economics first.',
]
if self.review_mode == 'Cash-Risk Review':
notes[2] = 'If current velocity stays flat, estimate how much cash remains tied up and how soon markdown pressure becomes unavoidable.'
return notes
def _action_ladder(self) -> List[str]:
actions = [
'Reorder now for the highest-confidence stockout risks only after checking lead time and true available stock.',
'Expedite, transfer, or rebalance inventory before expanding paid demand against a fragile SKU.',
'Slow buying or pause replenishment for SKUs that show overstock or aging without a convincing recovery path.',
'Use bundle, markdown, liquidation, or channel-shift plays deliberately instead of waiting for passive sell-through.',
]
if self.review_mode == 'Pre-Promo Risk Review':
actions[1] = 'Reduce promo intensity or reroute spend if hero inventory cannot safely support the planned demand spike.'
return actions
def _assumptions(self) -> List[str]:
notes = [
'This brief is heuristic and depends on the stock, velocity, and supply notes the user provided.',
'Revenue-at-risk and cash-at-risk framing is directional unless the inputs are complete and clean.',
'PO, transfer, markdown, liquidation, and media decisions should remain human-approved.',
]
missing = [signal for signal in ['Inbound / PO Status', 'Margin / Cash Pressure'] if signal not in self.signals]
if missing:
notes.append(f'Missing or lightly referenced signals: {_join(missing)}.')
return notes
def render(self) -> str:
lines: List[str] = []
lines.append('# Inventory Risk Radar')
lines.append('')
lines.append(f'**Review mode:** {self.review_mode}')
lines.append(f'**Signals referenced:** {_join(self.signals)}')
lines.append(f'**Priority risk types:** {_join(self.risk_types)}')
lines.append('**Method note:** This is a heuristic inventory-risk brief. No live ERP, WMS, supplier portal, or forecasting engine was accessed.')
lines.append('')
lines.append('## Inventory Risk Dashboard')
lines.append('| Risk type | Why it matters | First check | Default action path |')
lines.append('|---|---|---|---|')
for row in self._dashboard_rows():
lines.append(f'| {row["risk"]} | {row["why"]} | {row["check"]} | {row["action"]} |')
lines.append('')
lines.append('## Days of Cover Summary')
for note in self._coverage_notes():
lines.append(f'- {note}')
lines.append('')
lines.append('## SKU Action Watchlist')
for item in self._watchlist():
lines.append(f'- {item}')
lines.append('')
lines.append('## Scenario Notes')
for item in self._scenario_notes():
lines.append(f'- {item}')
lines.append('')
lines.append('## Action Ladder')
for idx, item in enumerate(self._action_ladder(), 1):
lines.append(f'{idx}. {item}')
lines.append('')
lines.append('## Assumptions and Limits')
for note in self._assumptions():
lines.append(f'- {note}')
return '\n'.join(lines)
def handle(user_input: RadarInput) -> str:
return InventoryRiskRadar(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import InventoryRiskRadar, handle
def test_review_mode_detection_prepromo():
radar = InventoryRiskRadar('Run a pre-promo risk review before our holiday campaign.')
assert radar.review_mode == 'Pre-Promo Risk Review'
def test_risk_detection_stockout_and_inbound():
radar = InventoryRiskRadar('Our hero SKU has low stock, only a few days of cover, and the inbound PO may be delayed.')
assert 'Stockout Risk' in radar.risk_types
assert 'Inbound Delay Risk' in radar.risk_types
def test_signal_detection_inventory_velocity_supplier():
radar = InventoryRiskRadar('Check on-hand stock, sales velocity, supplier lead time, and cash tied up in excess inventory.')
assert 'On-hand / Available Stock' in radar.signals
assert 'Sales Velocity' in radar.signals
assert 'Lead Time / Supplier Reliability' in radar.signals
assert 'Margin / Cash Pressure' in radar.signals
def test_render_contains_sections():
output = handle('Create an inventory watchlist for stockout, overstock, aging, and promo-readiness risk ahead of our campaign.')
assert output.startswith('# Inventory Risk Radar')
assert '## Inventory Risk Dashboard' in output
assert '## SKU Action Watchlist' in output
assert '## Action Ladder' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Turn a product idea, new SKU, or collection launch into a channel-ready ecommerce launch brief, messaging matrix, readiness checklist, milestone timeline, KP...
---
name: new-product-launch-copilot
description: Turn a product idea, new SKU, or collection launch into a channel-ready ecommerce launch brief, messaging matrix, readiness checklist, milestone timeline, KPI framework, and risk watchlist. Use when operators, founders, growth leads, or consultants need structured launch planning across Shopify, Amazon, email, paid social, TikTok Shop, Xiaohongshu, or similar channels without live PM, ads, or analytics integrations.
---
# New Product Launch Copilot
## Overview
Use this skill to convert a rough launch idea into an execution-ready planning brief. It helps ecommerce teams align positioning, channel sequencing, asset readiness, launch timing, KPI definitions, and risk controls before go-live.
This MVP is heuristic. It does **not** create ads, publish listings, sync inventory, or access live project-management tools. It relies on the user's provided product notes, channel context, assets, and constraints.
## Trigger
Use this skill when the user wants to:
- prepare a launch brief for a new SKU, bundle, or seasonal collection
- turn product features into channel-ready messaging
- build a launch checklist, timeline, and dependency view
- spot launch-readiness gaps in assets, FAQ, support, analytics, or inventory
- define day-1, week-1, and launch-window KPIs before launch
### Example prompts
- "Create a launch plan for our new skincare serum"
- "Build a Shopify, email, and TikTok launch brief from these notes"
- "What are we missing before this Amazon bundle launch goes live?"
- "Turn this product idea into a multi-channel launch checklist"
## Workflow
1. Capture the launch objective, product promise, and target audience.
2. Map the likely launch channels, asset gaps, and operating dependencies.
3. Translate features into benefits, proof points, and channel hooks.
4. Build the readiness checklist, milestone plan, KPI frame, and risk watchlist.
5. Return a markdown launch brief that the team can execute and review.
## Inputs
The user can provide any mix of:
- product name, category, feature list, packaging notes, and price target
- launch goal such as first orders, demand validation, AOV growth, or waitlist building
- preferred channels such as Shopify, Amazon, email, paid social, TikTok Shop, Xiaohongshu, or creator outreach
- current assets such as PDP draft, imagery, video, UGC, FAQ, reviews, or influencer notes
- stock, budget, deadline, or compliance constraints
- competitor references or seasonal campaign context
## Outputs
Return a markdown launch brief with:
- launch thesis and positioning summary
- messaging matrix
- readiness checklist by workstream
- milestone timeline from pre-launch to post-launch
- KPI and risk framework
- post-launch learning loop
- assumptions, confidence notes, and limits
## Safety
- Do not claim access to live ads platforms, storefronts, or launch dashboards.
- Do not imply that the launch is ready just because a checklist exists.
- Keep claims, pricing, stock, and compliance decisions human-approved.
- Treat messaging and offer recommendations as draft strategy until reviewed.
- Downgrade certainty when proof assets, inventory, or tracking details are missing.
## Best-fit Scenarios
- DTC and marketplace teams launching a new SKU, bundle, or seasonal collection
- brands without a dedicated launch PM office
- operators who need a reusable launch playbook instead of scattered notes
- agencies or consultants creating first-pass launch plans for clients
## Not Ideal For
- enterprise launches with complex legal, medical, or cross-country governance
- workflows that require automated campaign building or listing creation
- launches with almost no product information or no basic asset inputs
- highly regulated claims where specialist approval is mandatory before messaging exists
## Example Output Pattern
A strong response should:
- explain the launch objective and the audience promise clearly
- bridge product features into channel-ready hooks and proof points
- show what must be ready by T-21, T-14, T-7, T-3, T-0, T+3, and T+7
- include KPI, owner, and risk thinking, not only copy ideas
- leave a visible assumptions block when key evidence is missing
## Acceptance Criteria
- Return markdown text.
- Include launch, checklist, timeline, KPI, and risk sections.
- Make advisory limits explicit.
- Keep the brief practical for ecommerce operators and growth teams.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import Any, Dict, List, Union
OBJECTIVE_RULES = {
'Acquire First Orders': ['first orders', 'go live', 'launch', 'rollout', 'sell now'],
'Validate Demand': ['validate', 'test demand', 'demand test', 'pilot', 'soft launch'],
'Increase AOV': ['aov', 'bundle', 'upsell', 'premium tier', 'higher basket'],
'Build Waitlist': ['waitlist', 'prelaunch', 'pre-launch', 'lead capture', 'collect leads'],
'Seasonal Campaign Support': ['seasonal', 'holiday', 'mother\'s day', 'gift', 'campaign', 'collection'],
}
CHANNEL_RULES = {
'Shopify / DTC Store': ['shopify', 'dtc', 'storefront', 'pdp'],
'Amazon': ['amazon', 'listing'],
'Email / CRM': ['email', 'crm', 'klaviyo'],
'Meta Ads': ['meta', 'facebook', 'instagram', 'paid social'],
'TikTok Shop / Short Video': ['tiktok', 'douyin', 'short video'],
'Xiaohongshu / Creator Seeding': ['xiaohongshu', 'rednote', 'creator', 'ugc', 'influencer'],
}
ASSET_RULES = {
'PDP / Listing Copy': ['pdp', 'listing', 'product page', 'title', 'bullets', 'description'],
'Images / Video': ['image', 'images', 'photo', 'photos', 'video', 'creative', 'demo'],
'UGC / Social Proof': ['ugc', 'review', 'reviews', 'testimonial', 'creator'],
'FAQ / Support': ['faq', 'support', 'customer support', 'chat', 'objection'],
'Tracking / Analytics': ['tracking', 'analytics', 'pixel', 'utm', 'measurement'],
'Inventory / Packaging': ['inventory', 'stock', 'packaging', 'warehouse', 'fulfillment'],
}
LaunchInput = Union[str, Dict[str, Any]]
def _score_rules(text: str, rules: Dict[str, List[str]]) -> Dict[str, int]:
return {name: sum(1 for keyword in keywords if keyword in text) for name, keywords in rules.items()}
def _join(items: List[str]) -> str:
return ', '.join(items) if items else 'None explicitly provided'
class NewProductLaunchCopilot:
def __init__(self, user_input: LaunchInput):
self.raw = user_input
self.text = self._normalize_input(user_input)
self.lower = self.text.lower()
self.objective = self._detect_objective()
self.channels = self._detect_channels()
self.assets = self._detect_assets()
def _normalize_input(self, user_input: LaunchInput) -> str:
if isinstance(user_input, dict):
chunks: List[str] = []
for key in ['product', 'objective', 'audience', 'channels', 'assets', 'constraints', 'notes']:
value = user_input.get(key)
if not value:
continue
if isinstance(value, list):
value = ', '.join(str(item) for item in value)
chunks.append(f'{key}: {value}')
return ' | '.join(chunks)
return str(user_input or '').strip()
def _detect_objective(self) -> str:
scores = _score_rules(self.lower, OBJECTIVE_RULES)
best = max(scores, key=scores.get)
return best if scores[best] > 0 else 'Acquire First Orders'
def _detect_channels(self) -> List[str]:
matched = [name for name, keywords in CHANNEL_RULES.items() if any(keyword in self.lower for keyword in keywords)]
return matched or ['Shopify / DTC Store', 'Email / CRM']
def _detect_assets(self) -> List[str]:
matched = [name for name, keywords in ASSET_RULES.items() if any(keyword in self.lower for keyword in keywords)]
base = matched or ['PDP / Listing Copy', 'Images / Video', 'Tracking / Analytics']
if 'Inventory / Packaging' not in base:
base.append('Inventory / Packaging')
return base[:5]
def _launch_thesis(self) -> List[str]:
thesis = [
'Lead with one clear promise tied to a real user problem, not a long list of features.',
'Translate feature claims into benefits and proof points before writing channel copy.',
'Make the go-live decision depend on readiness, not only on the calendar date.',
]
if self.objective == 'Validate Demand':
thesis[2] = 'Keep the launch scoped like a learning test so the team can measure demand quality before scaling.'
if self.objective == 'Build Waitlist':
thesis[2] = 'Bias the launch toward lead capture, proof gathering, and opt-in quality before the full sales push.'
if self.objective == 'Increase AOV':
thesis[1] = 'Frame the offer around bundle logic, premium step-up value, and objection handling that protects basket size.'
return thesis
def _messaging_rows(self) -> List[Dict[str, str]]:
return [
{
'angle': 'Hero Message',
'benefit': 'What the product solves fastest or most clearly for the target buyer.',
'proof': 'Ingredient, feature, demo, review, or use-case evidence that reduces disbelief.',
'channel': 'Homepage hero, PDP headline, launch email subject, creator brief opening.',
},
{
'angle': 'Objection Handling',
'benefit': 'Address price, fit, complexity, shipping, or trust concerns before they stall action.',
'proof': 'FAQ, comparison chart, shipping note, guarantee, or support macro.',
'channel': 'PDP module, support prep, ad comments, retention email.',
},
{
'angle': 'Offer Logic',
'benefit': 'Explain why the buyer should act now and which option is best for them.',
'proof': 'Bundle savings, launch-only bonus, tiered pricing, waitlist perk, or stock note.',
'channel': 'Offer banner, email CTA, paid-social hook, creator talking point.',
},
]
def _checklist(self) -> List[str]:
items = [
'Confirm PDP or listing copy, value hierarchy, pricing, and main objections.',
'Review images, demo video, UGC, and any claim-sensitive creative before publishing.',
'Lock FAQ, customer-support macros, and internal escalation notes for launch week.',
'Verify tracking, UTMs, attribution assumptions, and launch reporting ownership.',
'Check inventory, packaging, shipping promises, and any constrained components before spend ramps.',
]
if self.objective == 'Build Waitlist':
items.insert(0, 'Create a clear waitlist capture mechanic and define what happens after sign-up.')
return items[:6]
def _timeline(self) -> List[str]:
return [
'T-21: finalize positioning, launch objective, and must-have assets.',
'T-14: review copy, creative, FAQ, tracking plan, and inventory constraints.',
'T-7: confirm channel sequencing, owner responsibilities, and go/no-go blockers.',
'T-3: QA the live experience, links, offer logic, support coverage, and stock visibility.',
'T-0: monitor launch execution, top objections, attribution sanity, and inventory pressure.',
'T+3: capture early signal quality, channel performance, and urgent fixes.',
'T+7: document lessons, repeatable assets, and what should scale, pause, or revise.',
]
def _kpis(self) -> List[str]:
items = [
'Day-1: traffic quality, CTR, PDP engagement, add-to-cart rate, and support-ticket themes.',
'Week-1: conversion rate, first-order volume, CAC or ROAS quality, and stock health.',
'Launch window: contribution quality, refund risk, AOV impact, and repeatable channel signals.',
]
if self.objective == 'Build Waitlist':
items[0] = 'Day-1: waitlist opt-in rate, landing-page conversion, and source quality.'
if self.objective == 'Validate Demand':
items[1] = 'Week-1: sample order quality, conversion by audience, and whether objection patterns repeat.'
if self.objective == 'Increase AOV':
items[2] = 'Launch window: bundle take rate, upsell attachment rate, and margin mix quality.'
return items
def _risks(self) -> List[str]:
items = [
'Weak proof points can make the product message sound interesting but not credible enough to convert.',
'Asset completeness can hide operational gaps, especially FAQ, support prep, and measurement ownership.',
'A fixed go-live date can create false urgency if inventory or creative quality is still unstable.',
'Tracking gaps can make the team overreact to noisy early data.',
]
if 'Inventory / Packaging' not in self.assets:
items.append('Inventory and packaging assumptions were not clearly referenced, so launch-readiness confidence is lower.')
return items[:5]
def _assumptions(self) -> List[str]:
return [
'This brief is heuristic and depends on the product, audience, and asset notes the user supplied.',
'Creative execution quality, legal review, and live media performance still require human ownership.',
'Stock, claim, pricing, and launch-delay decisions should remain human-approved.',
]
def render(self) -> str:
lines: List[str] = []
lines.append('# New Product Launch Brief')
lines.append('')
lines.append(f'**Launch objective:** {self.objective}')
lines.append(f'**Channels referenced:** {_join(self.channels)}')
lines.append(f'**Assets referenced:** {_join(self.assets)}')
lines.append('**Method note:** This is a heuristic launch-planning brief. No live storefront, ads account, project-management board, or analytics stack was accessed.')
lines.append('')
lines.append('## Launch Brief')
for bullet in self._launch_thesis():
lines.append(f'- {bullet}')
lines.append('')
lines.append('## Messaging Matrix')
lines.append('| Angle | Benefit to land | Proof to include | Best-fit channel use |')
lines.append('|---|---|---|---|')
for row in self._messaging_rows():
lines.append(f'| {row["angle"]} | {row["benefit"]} | {row["proof"]} | {row["channel"]} |')
lines.append('')
lines.append('## Readiness Checklist')
for idx, item in enumerate(self._checklist(), 1):
lines.append(f'{idx}. {item}')
lines.append('')
lines.append('## Timeline and Dependencies')
for item in self._timeline():
lines.append(f'- {item}')
lines.append('')
lines.append('## KPI Framework')
for item in self._kpis():
lines.append(f'- {item}')
lines.append('')
lines.append('## Risk Watchlist')
for item in self._risks():
lines.append(f'- {item}')
lines.append('')
lines.append('## Post-Launch Learning Loop')
lines.append('- Capture what message, channel, offer, and objection pattern created the cleanest demand signal.')
lines.append('- Save one reusable launch asset set and one reusable checklist improvement for the next launch.')
lines.append('- Separate signal quality from hype so the next decision is grounded in evidence.')
lines.append('')
lines.append('## Assumptions and Limits')
for note in self._assumptions():
lines.append(f'- {note}')
return '\n'.join(lines)
def handle(user_input: LaunchInput) -> str:
return NewProductLaunchCopilot(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import NewProductLaunchCopilot, handle
def test_objective_detection_waitlist():
copilot = NewProductLaunchCopilot('We need a pre-launch waitlist and lead capture plan for this new product.')
assert copilot.objective == 'Build Waitlist'
def test_channel_detection_shopify_email_tiktok():
copilot = NewProductLaunchCopilot('Launch this SKU across Shopify, email, and TikTok with creator support.')
assert 'Shopify / DTC Store' in copilot.channels
assert 'Email / CRM' in copilot.channels
assert 'TikTok Shop / Short Video' in copilot.channels
def test_asset_detection_tracking_and_inventory():
copilot = NewProductLaunchCopilot('We have a PDP draft, product photos, FAQ notes, tracking setup, and inventory constraints.')
assert 'PDP / Listing Copy' in copilot.assets
assert 'Images / Video' in copilot.assets
assert 'FAQ / Support' in copilot.assets
assert 'Tracking / Analytics' in copilot.assets
def test_render_contains_sections():
output = handle('Create a Shopify, email, and TikTok launch brief for a seasonal bundle with UGC, FAQ, and launch KPIs.')
assert output.startswith('# New Product Launch Brief')
assert '## Launch Brief' in output
assert '## Messaging Matrix' in output
assert '## Readiness Checklist' in output
assert '## KPI Framework' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Audit an ecommerce catalog, spot SKU sprawl, price and attribute coverage gaps, hero dependence, long-tail bloat, and duplicate-risk clusters, then turn roug...
---
name: assortment-scout
description: Audit an ecommerce catalog, spot SKU sprawl, price and attribute coverage gaps, hero dependence, long-tail bloat, and duplicate-risk clusters, then turn rough catalog notes or CSV exports into keep-add-expand-merge-retire recommendations and a 30-day merchandising brief. Use when merchandisers, category managers, marketplace sellers, or consultants need assortment planning support without live ERP, PIM, or marketplace APIs.
---
# Assortment Scout
## Overview
Use this skill to turn catalog notes, export summaries, and merchandising goals into a practical assortment review. It is built for operators who need a fast decision layer for what to keep, expand, bundle, merge, or retire.
This MVP is heuristic. It does **not** access live Shopify, Amazon, ERP, PIM, or marketplace systems. It relies on the user's provided catalog structure, product performance notes, and business constraints.
## Trigger
Use this skill when the user wants to:
- reduce SKU clutter or long-tail bloat
- identify price-band, feature, or variant coverage gaps
- review duplicate-risk or cannibalization concerns
- prepare a category review, seasonal line review, or catalog cleanup memo
- turn pasted catalog notes into a prioritized merchandising action brief
### Example prompts
- "Audit our catalog for SKU clutter and hero-product dependence"
- "Find assortment gaps across our travel accessories line"
- "Which products should we keep, merge, bundle, or retire?"
- "Create an assortment review from these catalog and margin notes"
## Workflow
1. Capture the review objective, such as cleanup, gap discovery, expansion planning, or seasonal review.
2. Normalize the likely assortment signals: revenue, margin, returns, inventory, and variant coverage.
3. Apply a portfolio lens across hero, core, seasonal, long-tail, and duplicate-risk products.
4. Highlight likely gap areas, overlap clusters, and execution priorities.
5. Return a markdown brief with keep-add-expand-merge-retire guidance and a 30-day plan.
## Inputs
The user can provide any mix of:
- catalog exports or summarized SKU lists
- category, subcategory, price, margin, and launch-age notes
- performance signals such as revenue, units, conversion, returns, ratings, or sell-through
- variant structure such as size, color, pack size, or material
- business goals such as premiumization, bundle strategy, entry-price coverage, or seasonal cleanup
- operating constraints such as shelf space, warehouse capacity, cash limits, or protected hero products
## Outputs
Return a markdown assortment brief with:
- assortment health summary
- scorecard lenses and evidence gaps
- coverage and gap map
- duplicate-risk or cannibalization watchlist
- keep-add-expand-merge-retire recommendations
- 30-day execution brief with likely owners
- assumptions, confidence notes, and limits
## Safety
- Do not claim access to live catalog or marketplace data.
- Treat cannibalization as an informed hypothesis, not proven causality.
- Do not auto-retire, merge, or reprice products.
- Downgrade recommendations when taxonomy, margin, or demand evidence is incomplete.
- Keep strategic SKU decisions human-approved.
## Best-fit Scenarios
- DTC or marketplace catalogs with roughly 30 to 2,000 active SKUs
- regular category reviews, quarterly assortment planning, or pre-promo cleanup
- teams that want a lighter decision layer than a full merchandise-planning suite
- consultants who need a fast first-pass assortment memo
## Not Ideal For
- store-level planogram planning for large physical retail networks
- businesses with no structured catalog or product taxonomy at all
- workflows that need automatic listing edits, delisting, or system sync
- highly regulated approvals where assortment change requires formal governance
## Example Output Pattern
A strong response should:
- show the likely assortment shape, not just list products
- separate hero, core, seasonal, long-tail, and duplicate-risk logic
- explain where the catalog is overbuilt or under-covered
- recommend next actions with impact, confidence, and owner hints
- include a short assumptions block when the evidence is partial
## Acceptance Criteria
- Return markdown text.
- Include health, gap, recommendation, and execution sections.
- Make the advisory framing explicit.
- Keep the brief practical for merchandisers and ecommerce operators.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import Any, Dict, List, Union
OBJECTIVE_RULES = {
'SKU Cleanup': ['cleanup', 'rationalize', 'prune', 'retire', 'long-tail', 'long tail', 'sku clutter', 'bloat', 'duplicate'],
'Gap Discovery': ['gap', 'whitespace', 'white space', 'coverage', 'missing', 'price ladder', 'assortment gap'],
'Expansion Planning': ['expand', 'extension', 'premium', 'premiumize', 'bundle', 'add sku', 'line extension', 'expand range'],
'Seasonal Review': ['seasonal', 'holiday', 'summer', 'winter', 'back to school', 'gifting', 'campaign'],
}
SIGNAL_RULES = {
'Revenue / Units': ['revenue', 'sales', 'gmv', 'units', 'velocity', 'sell-through', 'sell through', 'conversion'],
'Margin / Cost': ['margin', 'profit', 'cost', 'gross margin', 'contribution'],
'Returns / Reviews': ['return', 'refund', 'review', 'rating', 'score', 'feedback'],
'Inventory / Markdown': ['inventory', 'stock', 'markdown', 'clearance', 'aged', 'aging'],
'Variants / Coverage': ['size', 'color', 'variant', 'attribute', 'pack', 'material', 'coverage', 'price band'],
}
CONSTRAINT_RULES = {
'Warehouse / Cash Constraint': ['warehouse', 'cash', 'capacity', 'shelf', 'space'],
'Strategic Hero Protection': ['hero', 'flagship', 'protected line', 'core line', 'brand'],
'Seasonal Timing': ['seasonal', 'holiday', 'campaign', 'launch window', 'promo'],
}
SEGMENT_NOTES = {
'hero': 'Protect proven traffic and conversion anchors before pruning the catalog around them.',
'core': 'Maintain the dependable volume and margin base that stabilizes the category.',
'seasonal': 'Treat seasonal items as opportunity bets, not permanent assortment defaults.',
'long-tail': 'Challenge slow movers that add complexity without enough reach, margin, or strategic value.',
'duplicate-risk': 'Group near-identical products and variants to test whether they deserve separate shelf space.',
}
ActionInput = Union[str, Dict[str, Any]]
def _score_rules(text: str, rules: Dict[str, List[str]]) -> Dict[str, int]:
return {name: sum(1 for keyword in keywords if keyword in text) for name, keywords in rules.items()}
def _join_list(items: List[str]) -> str:
return ', '.join(items) if items else 'None explicitly provided'
class AssortmentScout:
def __init__(self, user_input: ActionInput):
self.raw = user_input
self.text = self._normalize_input(user_input)
self.lower = self.text.lower()
self.objective = self._detect_objective()
self.signals = self._detect_signals()
self.constraints = self._detect_constraints()
self.segments = ['hero', 'core', 'seasonal', 'long-tail', 'duplicate-risk']
def _normalize_input(self, user_input: ActionInput) -> str:
if isinstance(user_input, dict):
chunks: List[str] = []
for key in ['objective', 'catalog_scope', 'signals', 'constraints', 'notes']:
value = user_input.get(key)
if not value:
continue
if isinstance(value, list):
value = ', '.join(str(item) for item in value)
chunks.append(f'{key}: {value}')
return ' | '.join(chunks)
return str(user_input or '').strip()
def _detect_objective(self) -> str:
scores = _score_rules(self.lower, OBJECTIVE_RULES)
best = max(scores, key=scores.get)
return best if scores[best] > 0 else 'SKU Cleanup'
def _detect_signals(self) -> List[str]:
matched = [name for name, keywords in SIGNAL_RULES.items() if any(keyword in self.lower for keyword in keywords)]
return matched or ['Revenue / Units', 'Margin / Cost', 'Variants / Coverage']
def _detect_constraints(self) -> List[str]:
matched = [name for name, keywords in CONSTRAINT_RULES.items() if any(keyword in self.lower for keyword in keywords)]
base = matched or ['Strategic Hero Protection']
if 'Warehouse / Cash Constraint' not in base:
base.append('Warehouse / Cash Constraint')
return base[:3]
def _scorecard_rows(self) -> List[Dict[str, str]]:
rows = [
{
'lens': 'Hero Dependence',
'inspect': 'Revenue concentration in top products, launch age, and margin resilience.',
'why': 'A weak bench behind hero SKUs makes growth fragile and limits assortment flexibility.',
},
{
'lens': 'Tail Bloat',
'inspect': 'Long-tail units, markdown frequency, support load, and warehouse complexity.',
'why': 'Low-value tail items often consume attention and cash without creating net incremental demand.',
},
{
'lens': 'Coverage Gaps',
'inspect': 'Price ladder, use case ladder, variant balance, and entry-to-premium spacing.',
'why': 'A catalog can look large while still missing practical buying paths for important segments.',
},
{
'lens': 'Duplicate Risk',
'inspect': 'Near-identical variants, overlapping claims, and products with similar price and demand profiles.',
'why': 'Overlap can create internal competition that raises costs without enough incremental revenue.',
},
]
return rows
def _gap_map(self) -> List[str]:
items = [
'Review whether the price ladder jumps too sharply between entry, core, and premium offers.',
'Check if important use cases are uncovered while low-value variants multiply inside the same use case.',
'Confirm whether size, color, pack size, or material variants reflect real demand rather than internal preference.',
]
if self.objective == 'Gap Discovery':
items.insert(0, 'Prioritize white-space opportunities where demand intent is clear but current coverage is thin.')
if self.objective == 'Expansion Planning':
items.insert(0, 'Look for bundle, premium, or adjacent-line opportunities that extend the hero logic instead of fragmenting it.')
return items[:4]
def _watchlist(self) -> List[str]:
items = [
'Flag SKU clusters with similar price, promise, and audience but weak differentiation in margin or demand quality.',
'Question variant families that expanded faster than evidence, especially color or pack-size proliferation.',
'Escalate any product that underperforms yet remains protected by habit instead of strategic rationale.',
]
if 'Returns / Reviews' in self.signals:
items.append('Review whether high-return or low-rating items are masking a product-market or quality mismatch.')
return items[:4]
def _actions(self) -> List[str]:
actions = [
'Keep and defend the hero and core SKUs that anchor traffic, conversion, or margin quality.',
'Merge or simplify near-duplicate variants before adding new assortment complexity.',
'Retire, bundle, or repackage long-tail items that create operational drag without strategic upside.',
'Create one focused test for the highest-confidence white-space or premiumization opportunity.',
]
if self.objective == 'Gap Discovery':
actions[3] = 'Add one deliberate gap-filling test where the price ladder or use-case ladder is visibly incomplete.'
if self.objective == 'Expansion Planning':
actions[3] = 'Expand around the strongest hero logic with one adjacent line, bundle, or premium tier instead of broad SKU proliferation.'
if self.objective == 'Seasonal Review':
actions[2] = 'Reduce seasonal clutter early so the promo window is concentrated on the highest-conviction assortment.'
return actions
def _execution_brief(self) -> List[str]:
return [
'Week 1: confirm taxonomy, price bands, and which SKUs are strategically protected before any retire or merge discussion.',
'Week 2: review the duplicate-risk clusters and long-tail items with merch, ops, and inventory owners in the same room.',
'Week 3: approve one gap-fill or bundle test, plus one cleanup action that removes measurable complexity.',
'Week 4: compare pre-change and post-change signals, then lock the next assortment cycle based on evidence rather than anecdote.',
]
def _assumptions(self) -> List[str]:
notes = [
'This brief is heuristic and only as strong as the catalog, pricing, and performance notes supplied by the user.',
'Cannibalization is inferred from overlap patterns, not proven through controlled causal analysis.',
'Retire, merge, pricing, and supplier decisions should remain human-approved.',
]
missing = [signal for signal in ['Returns / Reviews', 'Inventory / Markdown'] if signal not in self.signals]
if missing:
notes.append(f'Missing or lightly referenced signals: {_join_list(missing)}.')
return notes
def render(self) -> str:
lines: List[str] = []
lines.append('# Assortment Scout Brief')
lines.append('')
lines.append(f'**Primary objective:** {self.objective}')
lines.append(f'**Signals referenced:** {_join_list(self.signals)}')
lines.append(f'**Operating constraints:** {_join_list(self.constraints)}')
lines.append('**Method note:** This is a heuristic assortment-planning brief. No live ERP, PIM, storefront, or marketplace data was accessed.')
lines.append('')
lines.append('## Assortment Health Summary')
lines.append('- Start by clarifying the role of each SKU: hero, core, seasonal, long-tail, or duplicate-risk.')
lines.append('- Treat assortment quality as a portfolio question, not a pure sales ranking exercise.')
lines.append('- If taxonomy or pricing logic is weak, downgrade any aggressive keep-add-retire recommendation to a hypothesis.')
lines.append('')
lines.append('## Scorecard Lenses')
lines.append('| Lens | What to inspect first | Why it matters |')
lines.append('|---|---|---|')
for row in self._scorecard_rows():
lines.append(f'| {row["lens"]} | {row["inspect"]} | {row["why"]} |')
lines.append('')
lines.append('## Coverage and Gap Map')
for idx, item in enumerate(self._gap_map(), 1):
lines.append(f'{idx}. {item}')
lines.append('')
lines.append('## Cannibalization Watchlist')
for bullet in self._watchlist():
lines.append(f'- {bullet}')
lines.append('')
lines.append('## Action Recommendations')
for idx, action in enumerate(self._actions(), 1):
lines.append(f'{idx}. {action}')
lines.append('')
lines.append('## 30-Day Execution Brief')
for idx, action in enumerate(self._execution_brief(), 1):
lines.append(f'{idx}. {action}')
lines.append('')
lines.append('## Segment Notes')
for segment in self.segments:
lines.append(f'- **{segment}:** {SEGMENT_NOTES[segment]}')
lines.append('')
lines.append('## Assumptions and Limits')
for note in self._assumptions():
lines.append(f'- {note}')
return '\n'.join(lines)
def handle(user_input: ActionInput) -> str:
return AssortmentScout(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import AssortmentScout, handle
def test_objective_detection_cleanup():
scout = AssortmentScout('We need to prune SKU clutter, rationalize duplicates, and retire weak long-tail products.')
assert scout.objective == 'SKU Cleanup'
def test_signal_detection_margin_reviews_and_variants():
scout = AssortmentScout('Review margin, return rate, product ratings, and size-color variant coverage for our fashion line.')
assert 'Margin / Cost' in scout.signals
assert 'Returns / Reviews' in scout.signals
assert 'Variants / Coverage' in scout.signals
def test_dict_input_supported():
output = handle({
'objective': 'find assortment gaps',
'signals': ['revenue', 'margin', 'inventory'],
'constraints': ['warehouse capacity'],
'notes': 'Need a category review before our seasonal cleanup.',
})
assert '# Assortment Scout Brief' in output
assert '## Coverage and Gap Map' in output
def test_render_contains_sections():
output = handle('Audit our catalog for hero dependence, duplicate-risk SKUs, price-band gaps, and long-tail bloat.')
assert output.startswith('# Assortment Scout Brief')
assert '## Assortment Health Summary' in output
assert '## Action Recommendations' in output
assert '## 30-Day Execution Brief' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Turn ecommerce support policies, ticket patterns, and channel context into reusable support flows, agent playbooks, macros, bot handoff logic, and governance...
---
name: support-flow-builder
description: Turn ecommerce support policies, ticket patterns, and channel context into reusable support flows, agent playbooks, macros, bot handoff logic, and governance notes for CX leaders, support managers, and operations teams. Use when designing refund, return, delivery-delay, payment-failure, VIP escalation, or bot-to-human workflows without live helpdesk integrations.
---
# Support Flow Builder
## Overview
Use this skill to convert support-policy context and recurring ticket themes into a structured service workflow. It is designed for CX and ecommerce operations teams that need repeatable flows, safer answers, and faster onboarding.
This MVP is documentation-first. It does **not** connect to live helpdesks, edit production bots, or inspect real ticket systems. It applies a policy-grounded flow template and produces channel-ready outputs for review.
## Trigger
Use this skill when the user wants to:
- create or clean up a support flow for a common ticket type
- turn policies and rough ticket patterns into decision trees and agent playbooks
- generate macros or canned responses for email, chat, or marketplace messaging
- design bot handoff requirements and escalation logic
- identify policy gaps or governance risks in current support operations
### Example prompts
- "Build a refund flow for our ecommerce support team"
- "Create macros and escalation logic for delivery-delay tickets"
- "Turn our return policy into a live-chat playbook"
- "Design a bot-to-human handoff for payment failure cases"
## Workflow
1. Capture the support scenario, channels, and policy constraints.
2. Translate the request into decision points, required checks, and exception handling.
3. Build the core flow and define escalation boundaries.
4. Generate agent-facing macros and bot handoff fields.
5. Return a markdown pack that a CX manager can review and adapt.
## Inputs
The user can provide any mix of:
- ticket summaries or transcript snippets
- support policies, SLA notes, and approval rules
- target channels such as email, chat, marketplace IM, or call notes
- scenario goals, such as refund, return, delivery delay, payment failure, or VIP escalation
- team constraints, such as refund authority, warehouse dependencies, or multilingual support
## Outputs
Return a markdown pack with:
- intent summary
- support flow map
- agent playbook
- macros or canned responses
- bot handoff fields
- governance and QA notes
- knowledge gaps to document next
## Safety
- Do not invent policy authority the team has not provided.
- Mark sensitive, legal, fraud, or compensation-heavy cases for human review.
- Keep production automation changes out of scope.
- State clearly when policy gaps prevent a clean decision tree.
## Examples
### Example 1
Input: return policy notes and recent delivery-delay tickets.
Output: produce a flow map, escalation points, and copy-ready chat or email macros.
### Example 2
Input: payment-failure scenario for bot handoff design.
Output: define intake questions, required fields, escalation triggers, and agent response blocks.
## Acceptance Criteria
- Return markdown text.
- Include the flow, macro, and governance sections.
- Keep the output usable by managers and frontline agents.
- Flag policy gaps or sensitive-review points explicitly.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import Dict, List
SCENARIO_LIBRARY: Dict[str, Dict[str, object]] = {
'Refund Request': {
'keywords': ['refund', 'money back', 'refund request'],
'summary': 'Customers want a refund decision, timeline clarity, and confidence that the policy is applied consistently.',
'checks': ['order ID', 'purchase date', 'refund reason', 'item condition', 'payment method'],
'approval': ['high-value refunds', 'manual compensation', 'fraud or abuse suspicion'],
},
'Return / Exchange': {
'keywords': ['return', 'exchange', 'swap', 'replace'],
'summary': 'Customers need eligibility guidance, logistics instructions, and a clear next step for return or exchange handling.',
'checks': ['order ID', 'purchase date', 'product category', 'opened or used status', 'preferred outcome'],
'approval': ['out-of-policy exceptions', 'damaged-item disputes', 'inventory override'],
},
'Delivery Delay': {
'keywords': ['delivery delay', 'delay', 'late', 'tracking', 'shipment', 'not arrived'],
'summary': 'Customers need status clarity, reassurance, and consistent escalation when the shipment is late or unclear.',
'checks': ['order ID', 'tracking status', 'carrier milestone', 'promised date', 'customer urgency'],
'approval': ['reshipment', 'compensation offer', 'warehouse escalation'],
},
'Payment Failure': {
'keywords': ['payment failure', 'payment failed', 'declined', 'card issue', 'checkout payment'],
'summary': 'Customers need a fast diagnosis path that separates payment-method issues from merchant-side outages or policy blocks.',
'checks': ['order ID if created', 'payment method', 'error message', 'attempt timestamp', 'device or channel'],
'approval': ['suspected fraud', 'manual order creation', 'billing exception'],
},
'VIP Escalation': {
'keywords': ['vip', 'priority customer', 'high value customer', 'executive complaint'],
'summary': 'The team needs a high-touch path with tighter response time, ownership, and approval visibility.',
'checks': ['customer tier', 'recent order value', 'issue severity', 'promised SLA', 'assigned owner'],
'approval': ['non-standard compensation', 'public relations risk', 'founder or leadership review'],
},
'Bot Handoff': {
'keywords': ['bot handoff', 'human handoff', 'handoff', 'agent handoff'],
'summary': 'The flow must collect the right fields early and transfer context cleanly to a human agent.',
'checks': ['contact reason', 'order ID if available', 'customer language', 'uploaded proof', 'bot summary'],
'approval': ['policy conflict', 'sensitive complaint', 'repeat unresolved issue'],
},
}
CHANNEL_RULES = {
'Email': ['email'],
'Live Chat': ['chat', 'live chat', 'intercom', 'chatbot'],
'Marketplace IM': ['marketplace', 'im', 'amazon message', 'shop message'],
'Call Notes': ['call', 'phone'],
}
class SupportFlowBuilder:
def __init__(self, text: str):
self.text = (text or '').strip()
self.lower = self.text.lower()
self.scenario = self._detect_scenario()
self.channels = self._detect_channels()
def _detect_scenario(self) -> str:
for scenario in ['Return / Exchange', 'Refund Request', 'Delivery Delay', 'Payment Failure', 'VIP Escalation', 'Bot Handoff']:
if any(keyword in self.lower for keyword in SCENARIO_LIBRARY[scenario]['keywords']):
return scenario
return 'Refund Request'
def _detect_channels(self) -> List[str]:
channels = [name for name, keywords in CHANNEL_RULES.items() if any(keyword in self.lower for keyword in keywords)]
return channels or ['Email', 'Live Chat']
def _flow_steps(self) -> List[str]:
details = SCENARIO_LIBRARY[self.scenario]
checks = details['checks']
return [
f'Intake: collect {", ".join(checks[:3])} before promising an outcome.',
f'Qualification: verify policy fit across {", ".join(checks[3:])}.',
'Decision: approve, deny, route to another team, or request missing information with a clear reason.',
'Closure: confirm timeline, owner, and the next customer-visible update.',
]
def _agent_playbook(self) -> List[str]:
details = SCENARIO_LIBRARY[self.scenario]
checks = details['checks']
return [
f'Ask only the minimum required questions first: {", ".join(checks)}.',
'State the policy or rule in plain language before giving the resolution.',
'If the case is outside policy, explain the exception path instead of improvising a promise.',
'Leave an internal note that captures evidence, decision, and next owner.',
]
def _macro_for_channel(self, channel: str) -> str:
scenario_phrase = self.scenario.lower()
if channel == 'Email':
return f'Subject: Update on your {scenario_phrase} request\n\nHi there, thanks for contacting us. I am reviewing your case and will confirm the next step as soon as I verify the required details.'
if channel == 'Live Chat':
return f'Thanks for reaching out. I can help with this {scenario_phrase} request. First, please share your order ID and the key detail that best describes the issue.'
if channel == 'Marketplace IM':
return f'Thanks for your message. Please send your order number and a short note about the {scenario_phrase} issue so we can review the correct policy path.'
return f'Call-note template: Confirm the customer goal, restate the {scenario_phrase} policy path, and log the next promised update.'
def _knowledge_gaps(self) -> List[str]:
base = ['Document the exact approval owner for edge cases so frontline agents do not guess.']
if self.scenario in ('Refund Request', 'Return / Exchange'):
base.append('Clarify any category exclusions, opened-item rules, and refund timing promises.')
if self.scenario == 'Delivery Delay':
base.append('Document compensation thresholds and warehouse-escalation criteria by delay severity.')
if self.scenario == 'Payment Failure':
base.append('Document what agents may suggest safely versus what must be escalated to payments or fraud review.')
if self.scenario == 'Bot Handoff':
base.append('Define the required structured fields so the bot does not hand off empty context.')
return base
def render(self) -> str:
details = SCENARIO_LIBRARY[self.scenario]
lines: List[str] = []
lines.append('# Support Flow Design Pack')
lines.append('')
lines.append(f'**Primary scenario:** {self.scenario}')
lines.append(f'**Channels:** {", ".join(self.channels)}')
lines.append('**Method note:** This is a policy-grounded support design pack. No live helpdesk, bot builder, or ticket analytics system was accessed.')
lines.append('')
lines.append('## Intent Summary')
lines.append(f'- {details["summary"]}')
lines.append('- Design the flow so frontline agents know what to collect, what to decide, and when to escalate.')
lines.append('')
lines.append('## Support Flow Map')
for idx, step in enumerate(self._flow_steps(), 1):
lines.append(f'{idx}. {step}')
lines.append('')
lines.append('## Agent Playbook')
for bullet in self._agent_playbook():
lines.append(f'- {bullet}')
lines.append('')
lines.append('## Macros / Canned Responses')
for channel in self.channels:
lines.append(f'### {channel}')
lines.append(self._macro_for_channel(channel))
lines.append('')
lines.append('## Bot Handoff Fields')
lines.append('- Customer name or handle')
lines.append(f'- Required scenario checks: {", ".join(details["checks"])}')
lines.append('- Existing transcript summary and any uploaded proof')
lines.append('- Urgency, sentiment, and requested outcome')
lines.append('')
lines.append('## Governance and QA')
lines.append(f'- Human review required for: {", ".join(details["approval"])}.')
lines.append('- Review macro tone, legal wording, and compensation promises before rollout.')
lines.append('- Version the flow when policy changes so new agents are not trained on stale guidance.')
lines.append('')
lines.append('## Knowledge Gaps to Close Next')
for bullet in self._knowledge_gaps():
lines.append(f'- {bullet}')
return '\n'.join(lines)
def handle(user_input: str) -> str:
return SupportFlowBuilder(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import SupportFlowBuilder, handle
def test_scenario_detection_delivery_delay():
builder = SupportFlowBuilder('Create an email and chat flow for delivery delay tickets')
assert builder.scenario == 'Delivery Delay'
def test_channel_detection_email_and_chat():
builder = SupportFlowBuilder('Need email and live chat macros for a refund workflow')
assert 'Email' in builder.channels
assert 'Live Chat' in builder.channels
def test_scenario_detection_payment_failure():
builder = SupportFlowBuilder('Design a bot handoff for payment failed support cases')
assert builder.scenario in ('Payment Failure', 'Bot Handoff')
def test_render_contains_key_sections():
output = handle('Build a return and exchange support flow with macros and governance notes')
assert output.startswith('# Support Flow Design Pack')
assert '## Support Flow Map' in output
assert '## Macros / Canned Responses' in output
assert '## Governance and QA' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Build payment-routing diagnostics, route matrices, retry and failover policies, rollout guardrails, and merchant-friendly planning briefs for payment, checko...
---
name: payment-route-optimizer
description: Build payment-routing diagnostics, route matrices, retry and failover policies, rollout guardrails, and merchant-friendly planning briefs for payment, checkout, finance, and cross-border ecommerce teams. Use when comparing PSPs, acquirers, local methods, authorization rate, processing cost, or chargeback-sensitive routing decisions without touching live payment traffic.
---
# Payment Route Optimizer
## Overview
Use this skill to turn payment-operations context into an offline routing strategy brief. It is best for merchants and product teams that need clearer routing logic, safer failover planning, and a readable rollout package.
This MVP is planning-oriented. It does **not** switch live traffic, move money, inspect raw PAN data, or replace compliance review. It translates merchant goals into route candidates, retry guardrails, and implementation notes.
## Trigger
Use this skill when the user wants to:
- improve authorization rate or payment success rate
- reduce payment processing cost without blindly sacrificing approval
- compare PSPs, acquirers, local methods, or failover setups
- prepare a routing policy review, outage plan, or market-launch payment strategy
- generate a rollout checklist for engineering, finance, or payment ops
### Example prompts
- "Help me improve card authorization in Brazil"
- "Create a payment failover plan before peak season"
- "We need a routing matrix that balances approval and fee cost"
- "Turn these PSP notes into a rollout playbook"
## Workflow
1. Capture the objective, markets, payment methods, and major constraints.
2. Normalize the current routing question into approval, cost, risk, or balanced mode.
3. Generate segment-level route candidates and backup paths.
4. Add retry rules, failover guardrails, and rollout sequencing.
5. Return a markdown planning brief for review by product, finance, and engineering.
## Inputs
The user can provide any mix of:
- payment-log summaries or pasted findings
- PSP, acquirer, local method, or fee-sheet context
- market, currency, issuer, BIN, device, or amount-band concerns
- goals around authorization, cost, fraud, 3DS friction, or resilience
- rollout constraints, such as human approval, engineering bandwidth, or peak-season risk
## Outputs
Return a markdown brief with:
- routing objective and planning mode
- baseline diagnostic
- suggested route matrix
- retry and failover policy
- rollout plan
- monitoring guardrails
- compliance and limitation notes
## Safety
- Do not claim to execute live routing changes.
- Keep raw card data out of scope and assume only masked or tokenized inputs.
- Treat compliance, PCI, fraud, and chargeback review as external control layers.
- Require human approval before any production policy is adopted.
## Examples
### Example 1
Input: merchant wants better card approval in Brazil and Europe with two PSPs.
Output: recommend a segment-level routing matrix, one-retry rule for soft declines, and a staged rollout with market-specific monitoring.
### Example 2
Input: payment team wants an outage fallback plan before peak season.
Output: generate timeout, soft-decline, and provider-outage failover guidance with rollback triggers.
## Acceptance Criteria
- Return markdown text.
- Include diagnosis, route matrix, retry or failover guidance, and rollout sections.
- Keep the output readable for non-engineers.
- Make the offline and approval-required boundary explicit.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import List
OBJECTIVE_RULES = {
'Approval-first': ['approval', 'authorization', 'success rate', 'acceptance'],
'Cost-first': ['cost', 'fee', 'mdr', 'cheaper', 'processing cost'],
'Risk-first': ['fraud', 'chargeback', '3ds', 'risk'],
'Balanced': ['balanced', 'tradeoff', 'blend', 'both'],
}
MODE_RULES = {
'Failover Planning': ['failover', 'fallback', 'outage', 'downtime', 'peak season', 'resilience'],
'Market Launch Planning': ['launch', 'new market', 'new country', 'expansion'],
'Baseline Route Review': ['route', 'routing', 'psp', 'acquirer', 'authorization', 'cost'],
}
MARKET_RULES = {
'US': ['us', 'usa', 'united states'],
'EU': ['eu', 'europe', 'european'],
'Brazil': ['brazil', 'br'],
'UK': ['uk', 'united kingdom', 'britain'],
'Mexico': ['mexico', 'mx'],
'APAC': ['apac', 'asia', 'singapore', 'japan', 'australia'],
}
METHOD_RULES = {
'Card': ['card', 'visa', 'mastercard', 'amex', 'credit card', 'debit card'],
'Wallet': ['wallet', 'paypal', 'apple pay', 'google pay'],
'Local Method': ['pix', 'ideal', 'klarna', 'bank transfer', 'local method'],
}
class PaymentRouteOptimizer:
def __init__(self, text: str):
self.text = (text or '').strip()
self.lower = self.text.lower()
self.objective = self._detect_objective()
self.mode = self._detect_mode()
self.markets = self._detect_markets()
self.methods = self._detect_methods()
def _detect_objective(self) -> str:
for objective in ['Approval-first', 'Cost-first', 'Risk-first', 'Balanced']:
if any(keyword in self.lower for keyword in OBJECTIVE_RULES[objective]):
return objective
return 'Balanced'
def _detect_mode(self) -> str:
for mode in ['Failover Planning', 'Market Launch Planning', 'Baseline Route Review']:
if any(keyword in self.lower for keyword in MODE_RULES[mode]):
return mode
return 'Baseline Route Review'
def _detect_markets(self) -> List[str]:
markets = [name for name, keywords in MARKET_RULES.items() if any(keyword in self.lower for keyword in keywords)]
return markets or ['Core markets']
def _detect_methods(self) -> List[str]:
methods = [name for name, keywords in METHOD_RULES.items() if any(keyword in self.lower for keyword in keywords)]
return methods or ['Card']
def _baseline_findings(self) -> List[str]:
findings = ['Quantify loss by segment before changing the routing policy so the team knows where approval or cost is leaking.']
if self.objective == 'Approval-first':
findings.append('Treat issuer, BIN, 3DS friction, and local-acquirer coverage as the first suspects.')
elif self.objective == 'Cost-first':
findings.append('Verify where the lowest-fee path still clears a safe approval floor before shifting volume.')
elif self.objective == 'Risk-first':
findings.append('Prioritize fraud exposure, chargeback tolerance, and 3DS support over raw approval lift.')
else:
findings.append('Balance approval, cost, and risk together so one metric is not optimized at the expense of the business.')
if self.mode == 'Failover Planning':
findings.append('Map provider outage, timeout, and soft-decline scenarios separately because they require different fallback logic.')
return findings
def _segment_pairs(self) -> List[str]:
pairs: List[str] = []
for market in self.markets[:2]:
for method in self.methods[:2]:
pairs.append(f'{market} | {method}')
if len(pairs) >= 3:
return pairs
return pairs or ['Core markets | Card']
def _primary_path(self, pair: str) -> str:
market, method = [part.strip() for part in pair.split('|')]
if method == 'Local Method':
return 'Use the strongest local-method provider first, with a backup only where the method supports it.'
if self.objective == 'Approval-first':
return f'Use the provider with the strongest issuer coverage and local acceptance signals for {market}.'
if self.objective == 'Cost-first':
return f'Use the lowest-fee acceptable provider for {market}, provided approval stays above the agreed floor.'
if self.objective == 'Risk-first':
return f'Use the provider with stronger 3DS, fraud controls, and manual-review hooks for {market}.'
return f'Use the best local performer as primary and reserve the cheaper or lower-risk path as conditional backup for {market}.'
def _backup_path(self, pair: str) -> str:
if self.mode == 'Failover Planning':
return 'Immediate backup on provider outage or timeout, with idempotency checks and rollback visibility.'
return 'Secondary PSP or acquirer for approved fallback cases only, not as a blanket retry rule.'
def _rationale(self, pair: str) -> str:
market, method = [part.strip() for part in pair.split('|')]
if method == 'Wallet':
return f'Wallet flows usually need low-friction continuity and a clear timeout or provider-failure plan in {market}.'
if method == 'Local Method':
return f'Local method performance often depends on country-specific coverage and settlement practicality in {market}.'
return f'Card performance in {market} usually needs issuer-level analysis instead of one global routing rule.'
def _retry_policy(self) -> List[str]:
return [
'Retry only soft declines or recoverable technical failures, not hard declines or suspected fraud cases.',
'Use at most one carefully scoped retry per segment unless data clearly supports a second attempt.',
'For failover on timeout, require idempotency-safe design and clear ownership of duplicate-payment risk.',
]
def _rollout_plan(self) -> List[str]:
return [
'Simulate the candidate policy on recent historical samples before any production change is proposed.',
'Start with one market, method, or amount band instead of a global flip.',
'Review authorization rate, fee delta, 3DS completion, and chargeback early signals before expanding volume.',
'Keep an explicit rollback trigger and named approver for each rollout phase.',
]
def render(self) -> str:
lines: List[str] = []
lines.append('# Payment Routing Optimization Brief')
lines.append('')
lines.append(f'**Primary objective:** {self.objective}')
lines.append(f'**Planning mode:** {self.mode}')
lines.append(f'**Markets referenced:** {", ".join(self.markets)}')
lines.append(f'**Methods referenced:** {", ".join(self.methods)}')
lines.append('**Method note:** This is an offline planning brief. No live routing engine or production traffic was touched.')
lines.append('')
lines.append('## Baseline Diagnostic')
for item in self._baseline_findings():
lines.append(f'- {item}')
lines.append('')
lines.append('## Suggested Route Matrix')
lines.append('| Segment | Primary path | Backup path | Why this segment matters |')
lines.append('|---|---|---|---|')
for pair in self._segment_pairs():
lines.append(f'| {pair} | {self._primary_path(pair)} | {self._backup_path(pair)} | {self._rationale(pair)} |')
lines.append('')
lines.append('## Retry and Failover Policy')
for idx, item in enumerate(self._retry_policy(), 1):
lines.append(f'{idx}. {item}')
lines.append('')
lines.append('## Rollout Plan')
for idx, item in enumerate(self._rollout_plan(), 1):
lines.append(f'{idx}. {item}')
lines.append('')
lines.append('## Monitoring and Guardrails')
lines.append('- Track authorization rate, provider timeout rate, fee basis points, 3DS completion, and chargeback trend by segment.')
lines.append('- Compare the new policy against a control baseline so success is not judged on anecdotes alone.')
lines.append('- Keep market-specific notes because a rule that works in one country may be harmful in another.')
lines.append('')
lines.append('## Compliance and Limits')
lines.append('- Assume only masked or tokenized payment data is in scope.')
lines.append('- PCI, fraud review, scheme rules, and legal approval are outside this skill and must be handled separately.')
lines.append('- Human approval is required before any routing, retry, or failover rule is adopted in production.')
return '\n'.join(lines)
def handle(user_input: str) -> str:
return PaymentRouteOptimizer(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import PaymentRouteOptimizer, handle
def test_objective_detection_approval():
optimizer = PaymentRouteOptimizer('Need better authorization rate in Brazil card payments')
assert optimizer.objective == 'Approval-first'
def test_mode_detection_failover():
optimizer = PaymentRouteOptimizer('Create a failover plan before peak season for our PSP stack')
assert optimizer.mode == 'Failover Planning'
def test_market_and_method_detection():
optimizer = PaymentRouteOptimizer('Review card and PIX routing in Brazil and Europe')
assert 'Brazil' in optimizer.markets
assert 'EU' in optimizer.markets
assert 'Card' in optimizer.methods
assert 'Local Method' in optimizer.methods
def test_render_contains_key_sections():
output = handle('We need a balanced routing matrix with retry guidance and rollout guardrails')
assert output.startswith('# Payment Routing Optimization Brief')
assert '## Suggested Route Matrix' in output
assert '## Retry and Failover Policy' in output
assert '## Rollout Plan' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Turn ecommerce exports, KPI notes, and natural-language business questions into metric alignment notes, anomaly diagnoses, operator-ready summaries, and prio...
---
name: commerce-bi-copilot
description: Turn ecommerce exports, KPI notes, and natural-language business questions into metric alignment notes, anomaly diagnoses, operator-ready summaries, and prioritized next actions for founders, operators, and analysts. Use when reviewing GMV, net revenue, ROAS, refund rate, inventory health, channel mix, or campaign performance without live BI connectors or SQL access.
---
# Commerce BI Copilot
## Overview
Use this skill to convert fragmented commerce data context into an operator-friendly insight brief. It is designed for teams that need quick explanations, not a heavyweight dashboard rebuild.
This MVP is heuristic. It does **not** access live warehouses, ad APIs, ERP systems, or real spreadsheets. Instead, it applies a commerce metric dictionary, anomaly checklist, and action-planning framework to the user's provided notes.
## Trigger
Use this skill when the user wants to:
- explain why a KPI moved up or down
- prepare a daily, weekly, campaign, or executive business review
- align teams on metric definitions such as GMV, net revenue, ROAS, MER, or refund rate
- turn rough exports or pasted KPI notes into a concise action brief
- produce follow-up questions for an analyst, founder, or agency client
### Example prompts
- "Why did GMV drop 12% this week?"
- "Create a weekly ecommerce business review from these KPI notes"
- "Help me explain falling ROAS after our spring promotion"
- "Turn these Shopify, Meta, and refund notes into an executive summary"
## Workflow
1. Capture the business question, time frame, and referenced channels.
2. Normalize the likely metric set and call out any definition ambiguity.
3. Build a short driver tree across traffic, conversion, pricing, refunds, inventory, and mix.
4. Produce prioritized drill-downs and next actions.
5. Return a markdown brief that a founder or operator can immediately use.
## Inputs
The user can provide any mix of:
- pasted KPI snapshots or rough metric notes
- mentions of data sources such as Shopify, Amazon, Meta Ads, Google Ads, GA4, ERP, or CRM
- campaign or calendar context, such as promotions, launches, or stockouts
- business questions about revenue, efficiency, refunds, margin, or channel contribution
- audience context, such as founder update, operator review, or agency client recap
## Outputs
Return a markdown brief with:
- analysis mode and source assumptions
- KPI snapshot table
- likely driver tree
- recommended drill-downs
- prioritized next best actions
- executive-ready summary bullets
- assumptions and limitations
## Safety
- Do not pretend to read live numbers or source files.
- Surface metric-definition ambiguity when GMV, net revenue, refunds, or attribution may conflict.
- Avoid certainty when the input is partial or anecdotal.
- Keep budget, pricing, inventory, and operational decisions human-approved.
## Examples
### Example 1
Input: Shopify orders, Meta spend notes, and the question "Why did yesterday GMV fall?"
Output: identify a likely mix of traffic decline, conversion weakness, or stock issues, then recommend the next drill-downs and immediate operator actions.
### Example 2
Input: weekly KPI notes for channels, refunds, and top products.
Output: generate a compact weekly business brief with risks, wins, and next-week priorities.
## Acceptance Criteria
- Return markdown text.
- Include KPI, diagnosis, and action sections.
- Mention evidence gaps or metric ambiguity when relevant.
- Keep the output practical for operators and founders.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import Dict, List
MODE_RULES = {
'Anomaly Diagnosis': ['why', 'drop', 'down', 'decline', 'spike', 'anomaly', 'sudden', 'fell'],
'Weekly Review': ['weekly', 'monday', 'week over week', 'wow', 'leadership update'],
'Campaign Recap': ['campaign', 'promotion', 'promo', 'launch', 'sale', 'black friday'],
'Executive Summary': ['monthly', 'board', 'executive', 'founder update', 'leadership'],
'Daily Health Check': ['daily', 'today', 'yesterday', 'morning digest'],
}
METRIC_RULES: Dict[str, Dict[str, object]] = {
'GMV': {
'keywords': ['gmv', 'gross merchandise value', 'sales', 'revenue'],
'why': 'Track top-line demand and commercial momentum.',
'verify': 'Confirm whether the team means gross sales, order value before refunds, or another house definition.',
},
'Net Revenue': {
'keywords': ['net revenue', 'net sales', 'after refund', 'refund adjusted'],
'why': 'Separate real collected revenue from gross demand.',
'verify': 'Check refund timing, cancellation handling, and tax or shipping treatment.',
},
'ROAS / MER': {
'keywords': ['roas', 'mer', 'ad spend', 'marketing efficiency', 'cac'],
'why': 'Connect spend to revenue efficiency and budget quality.',
'verify': 'Confirm attribution window and whether blended or channel-level spend is in scope.',
},
'Conversion Rate': {
'keywords': ['conversion', 'cvr', 'checkout', 'sessions', 'traffic'],
'why': 'Explain whether the issue is demand volume or funnel quality.',
'verify': 'Check session source mix, landing-page quality, and checkout completion assumptions.',
},
'Refund Rate': {
'keywords': ['refund', 'returns', 'chargeback', 'cancel'],
'why': 'Protect net revenue and reveal post-purchase quality issues.',
'verify': 'Separate policy-driven refunds from defects, logistics, and friendly fraud patterns.',
},
'Inventory Health': {
'keywords': ['inventory', 'stock', 'stockout', 'oos', 'backorder'],
'why': 'Show whether demand was lost because the best sellers were unavailable.',
'verify': 'Review stockout duration, top-SKU share, and substitute-product behavior.',
},
}
CHANNEL_RULES = {
'Shopify': ['shopify'],
'Amazon': ['amazon'],
'TikTok Shop': ['tiktok', 'tik tok', 'douyin'],
'Meta Ads': ['meta', 'facebook ads', 'instagram ads'],
'Google Ads': ['google ads', 'google', 'youtube'],
'GA4 / Analytics': ['ga4', 'analytics', 'google analytics'],
'ERP / Inventory': ['erp', 'inventory', 'warehouse'],
'CRM / Retention': ['crm', 'klaviyo', 'email', 'retention'],
}
DRIVER_LIBRARY = {
'Traffic / Spend': {
'keywords': ['traffic', 'spend', 'sessions', 'click', 'impression', 'meta', 'google'],
'why': 'Volume shifts often start with paid or organic traffic quality and budget pacing.',
'slices': 'channel, campaign, audience, creative, landing page',
},
'Conversion / Funnel': {
'keywords': ['conversion', 'checkout', 'atc', 'cart', 'site', 'cvr'],
'why': 'Revenue can fall even when traffic is stable if the funnel weakens.',
'slices': 'device, landing page, PDP, cart, checkout step',
},
'Pricing / Promotion': {
'keywords': ['discount', 'promo', 'promotion', 'coupon', 'price', 'sale'],
'why': 'Promotions can inflate demand but hurt quality, margin, or post-promo retention.',
'slices': 'offer type, order size, new vs returning, campaign window',
},
'Refund / Post-purchase': {
'keywords': ['refund', 'return', 'chargeback', 'cancel'],
'why': 'Net revenue problems are often hidden in refunds, cancels, and service failure.',
'slices': 'reason code, SKU, fulfillment node, market, acquisition source',
},
'Inventory / Availability': {
'keywords': ['inventory', 'stock', 'stockout', 'oos', 'backorder'],
'why': 'High-demand SKUs can bottleneck the whole business when unavailable.',
'slices': 'SKU, variant, top sellers, stockout hours, region',
},
'Channel Mix': {
'keywords': ['channel', 'marketplace', 'shopify', 'amazon', 'mix', 'region'],
'why': 'A business may look worse overall when volume shifts toward lower-converting or lower-margin channels.',
'slices': 'channel, region, customer type, assisted vs direct',
},
}
class CommerceBICopilot:
def __init__(self, text: str):
self.text = (text or '').strip()
self.lower = self.text.lower()
self.mode = self._detect_mode()
self.metrics = self._detect_metrics()
self.channels = self._detect_channels()
self.drivers = self._detect_drivers()
def _detect_mode(self) -> str:
for mode in ['Anomaly Diagnosis', 'Weekly Review', 'Campaign Recap', 'Executive Summary', 'Daily Health Check']:
if any(keyword in self.lower for keyword in MODE_RULES[mode]):
return mode
return 'Daily Health Check'
def _detect_metrics(self) -> List[str]:
metrics = [name for name, data in METRIC_RULES.items() if any(keyword in self.lower for keyword in data['keywords'])]
return metrics or ['GMV', 'Conversion Rate', 'Refund Rate', 'Inventory Health']
def _detect_channels(self) -> List[str]:
channels = [name for name, keywords in CHANNEL_RULES.items() if any(keyword in self.lower for keyword in keywords)]
return channels or ['Orders / Storefront', 'Ads / Traffic']
def _detect_drivers(self) -> List[str]:
drivers = [name for name, data in DRIVER_LIBRARY.items() if any(keyword in self.lower for keyword in data['keywords'])]
ordered = []
for candidate in ['Traffic / Spend', 'Conversion / Funnel', 'Pricing / Promotion', 'Refund / Post-purchase', 'Inventory / Availability', 'Channel Mix']:
if candidate in drivers and candidate not in ordered:
ordered.append(candidate)
for candidate in ['Traffic / Spend', 'Conversion / Funnel', 'Inventory / Availability']:
if candidate not in ordered:
ordered.append(candidate)
return ordered[:4]
def _drill_downs(self) -> List[str]:
tasks = []
if 'GMV' in self.metrics or 'Net Revenue' in self.metrics:
tasks.append('Slice performance by channel, top campaign, top SKU family, and region against a recent baseline.')
if 'ROAS / MER' in self.metrics:
tasks.append('Compare spend pacing, attribution window assumptions, and new-vs-returning customer efficiency.')
if 'Refund Rate' in self.metrics:
tasks.append('Break refund or cancel reasons by SKU, market, and fulfillment node to isolate operational issues.')
if 'Inventory Health' in self.metrics:
tasks.append('Review top-SKU stockout windows and estimate lost demand during unavailable hours.')
if not tasks:
tasks.append('Reconcile the metric dictionary before diagnosing causes so the team is debating the same number.')
tasks.append('Document one plain-English answer to the business question before opening additional dashboard tabs.')
return tasks[:4]
def _next_actions(self) -> List[str]:
actions = ['Lock the metric definition for this review so GMV, net revenue, and refund-adjusted revenue are not mixed together.']
if 'Traffic / Spend' in self.drivers:
actions.append('Check campaign pacing and the last 7-day median before changing budget or blaming creative.')
if 'Conversion / Funnel' in self.drivers:
actions.append('Inspect the highest-leverage funnel step and look for device, checkout, or landing-page friction.')
if 'Inventory / Availability' in self.drivers:
actions.append('Protect the top-selling SKUs with alerts, substitute logic, or replenishment follow-up.')
if 'Refund / Post-purchase' in self.drivers:
actions.append('Create a short refund-reason review with ops and CX so net revenue leakage is owned by someone specific.')
if 'Pricing / Promotion' in self.drivers:
actions.append('Review whether the offer structure boosted low-quality orders or trained customers to wait for discounts.')
return actions[:4]
def _executive_brief(self) -> List[str]:
if self.mode == 'Weekly Review':
return [
'Frame the week around the biggest KPI movement, the strongest contributor, and the clearest risk entering next week.',
'Keep the leadership update to wins, losses, root-cause hypothesis, and the one action each owner should take next.',
'Use one chart per important story instead of a dashboard dump.',
]
if self.mode == 'Campaign Recap':
return [
'Summarize campaign lift, efficiency quality, refund or margin side effects, and inventory readiness for the next push.',
'Separate temporary promo volume from demand that is likely to persist.',
'Call out what should be repeated, reduced, or tested differently next time.',
]
if self.mode == 'Executive Summary':
return [
'Lead with the business outcome, not the chart mechanics.',
'Explain the main driver in one sentence and the decision it points to in the next sentence.',
'Keep open questions visible so leadership knows where the evidence is still incomplete.',
]
if self.mode == 'Anomaly Diagnosis':
return [
'Start with the likely driver cluster instead of listing every metric that moved.',
'State which slices would confirm or invalidate the current hypothesis fastest.',
'Do not recommend a major budget, pricing, or inventory change until the top suspect is checked against a baseline.',
]
return [
'Use a compact daily summary: what moved, what most likely caused it, and what the team should verify today.',
'Focus on exceptions and owner-ready tasks, not exhaustive dashboard narration.',
'Escalate only when the movement is material, persistent, or tied to a key launch or inventory risk.',
]
def render(self) -> str:
lines: List[str] = []
lines.append('# Commerce BI Brief')
lines.append('')
lines.append(f'**Analysis mode:** {self.mode}')
lines.append(f'**Channels referenced:** {", ".join(self.channels)}')
lines.append('**Method note:** This is a heuristic commerce-analysis brief. No live BI connector, warehouse, or spreadsheet was accessed.')
lines.append('')
lines.append('## Executive Summary')
lines.append('- Treat the input as a business question that needs a short answer, a likely explanation, and the next verification step.')
lines.append('- Align the metric dictionary first so the team is not debating conflicting versions of the same KPI.')
lines.append('')
lines.append('## KPI Snapshot')
lines.append('| Metric | Why it matters | First verification step |')
lines.append('|---|---|---|')
for metric in self.metrics:
data = METRIC_RULES[metric]
lines.append(f'| {metric} | {data["why"]} | {data["verify"]} |')
lines.append('')
lines.append('## Likely Driver Tree')
for driver in self.drivers:
data = DRIVER_LIBRARY[driver]
lines.append(f'### {driver}')
lines.append(f'- Why this matters: {data["why"]}')
lines.append(f'- First slices to check: {data["slices"]}')
lines.append('')
lines.append('## Recommended Drill-Downs')
for idx, item in enumerate(self._drill_downs(), 1):
lines.append(f'{idx}. {item}')
lines.append('')
lines.append('## Next Best Actions')
for idx, item in enumerate(self._next_actions(), 1):
lines.append(f'{idx}. {item}')
lines.append('')
lines.append('## Executive-Ready Brief')
for bullet in self._executive_brief():
lines.append(f'- {bullet}')
lines.append('')
lines.append('## Assumptions and Limits')
lines.append('- This output is only as strong as the KPI notes and definitions the user provided.')
lines.append('- Attribution, refund timing, and stock availability can all distort the apparent story if definitions are inconsistent.')
lines.append('- Budget moves, pricing changes, and operational decisions still require human review.')
return '\n'.join(lines)
def handle(user_input: str) -> str:
return CommerceBICopilot(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import CommerceBICopilot, handle
def test_mode_detection_anomaly():
copilot = CommerceBICopilot('Why did GMV drop yesterday after our promotion?')
assert copilot.mode == 'Anomaly Diagnosis'
def test_metric_detection_refund_and_inventory():
copilot = CommerceBICopilot('Need a weekly review of refunds, returns, and stockout issues')
assert 'Refund Rate' in copilot.metrics
assert 'Inventory Health' in copilot.metrics
def test_channel_detection_meta_and_shopify():
copilot = CommerceBICopilot('Turn our Shopify and Meta notes into an executive summary')
assert 'Shopify' in copilot.channels
assert 'Meta Ads' in copilot.channels
def test_render_contains_key_sections():
output = handle('Create a weekly ecommerce review for GMV, ROAS, refunds, and inventory')
assert output.startswith('# Commerce BI Brief')
assert '## KPI Snapshot' in output
assert '## Next Best Actions' in output
assert '## Executive-Ready Brief' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Manage and maintain a Markdown wiki with LLM Wiki Karpathy: inspect, repair, compile sources, add pages, answer queries, and lint for quality.
---
name: llm-wiki-karpathy
description: Inspired by a public workflow shared by Andrej Karpathy (@karpathy). Use when the user wants to check, repair, or grow a Markdown wiki backed by the LLM Wiki Karpathy runtime: inspect wiki health, compile missing source notes, clean placeholder pages, add concept/entity/synthesis pages, answer questions from the wiki and file the result back, or run deterministic multimodal wiki lint.
---
# LLM Wiki Karpathy
Use this skill to operate a Vault that is managed by the `LLM Wiki Karpathy` runtime.
The operating model is:
- `raw/` stores captured source material from the outside world
- `wiki/sources/` stores compiled source notes
- `wiki/outputs/` stores archived answer notes
- `wiki/concepts/` stores durable concept pages
- `wiki/entities/` stores durable entity pages
- `wiki/syntheses/` stores cross-source synthesis pages
- `wiki/_indexes/` stores generated collection indexes
- `wiki/index.md` stores the generated home index
- `wiki/log.md` stores the generated run log page
- `.llm-kb/` stores runtime state
- `.llm-kb/representations/` stores runtime-managed OCR, vision, metadata, and profiling artifacts for non-text raw assets
The runtime owns Vault I/O.
The agent owns understanding, synthesis, linking, and deciding which pages the wiki should gain or improve.
## Important Model
- Treat the Vault as runtime-managed.
- Use `kb_*` tools for all Vault reads and writes.
- Never modify files under `raw/`.
- Never write directly into `wiki/` or `.llm-kb/representations/` with generic file tools.
- Never fabricate IDs, canonical paths, raw hashes, representation paths, or `source_refs`.
- For text and structured-data raw files, use the direct source compile path.
- For PDF and image raw files, use the representation-first path: bundle the asset context, create any missing representations, then compile the source note.
- When a source note summarizes a PDF or image, keep `asset_paths` accurate and include visible review notes, usually `# Visual Notes`, whenever the source depends on a human or model review pass outside the stored representation files.
- The goal is not only to answer questions.
- The goal is to leave the wiki better structured after each meaningful interaction.
## Required Tools
- `kb_status`
- `kb_list_raw`
- `kb_read_raw`
- `kb_get_raw_asset`
- `kb_prepare_source`
- `kb_prepare_source_bundle`
- `kb_prepare_representation`
- `kb_upsert_representation`
- `kb_read_representations`
- `kb_upsert_source_note`
- `kb_prepare_output`
- `kb_upsert_output`
- `kb_prepare_derived_note`
- `kb_upsert_derived_note`
- `kb_search`
- `kb_read_notes`
- `kb_map_gaps`
- `kb_promote_gap`
- `kb_repair_source_ids`
- `kb_rebuild_indexes`
- `kb_lint`
If these tools are unavailable, say so clearly instead of pretending the workflow can proceed.
## Natural-Language Entry Points
Users do not need to name canonical actions.
Map short natural-language requests to the closest workflow.
The explicit prefix `$llm-wiki-karpathy` is recommended for maximum routing certainty, but it is not required when the request already clearly matches this skill.
- `check my wiki`, `look over my knowledge base`, `检查我的 wiki`, `总览检查`:
run `kb_status` plus `kb_lint`, report counts, the top issues, and the best next step
- `fill missing source notes`, `continue the backlog`, `补缺失 source notes`, `继续推进这份库`:
use `ingest-source`, call `kb_status`, then `kb_list_raw(changed_only=true)` with an explicit `limit` large enough for the batch, prioritize `missing_source_note`, honor directory/topic filters, compile in small batches, then `kb_rebuild_indexes`
- `clean up these pages`, `fix placeholder titles`, `只修 AI 相关内容`, `做一次维护清理`:
use `maintain-wiki`, start with `kb_lint`, then use `kb_search` plus `kb_read_notes` to target placeholder titles, placeholder summaries, placeholder open questions, placeholder related links, stale navigation, and missing high-value derived pages
- `add concept pages`, `add entity pages`, `add synthesis pages`, `what pages are missing?`, `补 concept/entity/synthesis`:
use `map-gaps`, search first, read the evidence, then call `kb_map_gaps`; use `kb_promote_gap` when a candidate can land directly, otherwise refine it through `kb_prepare_derived_note` plus `kb_upsert_derived_note`
- `repair source ids`, `repair manifest drift`, `修 source id / manifest 漂移`:
run `kb_repair_source_ids` as a dry run first, explain the plan, apply only if the repair set looks correct, then `kb_rebuild_indexes`
- `answer this from the wiki and save it back`, `问答并沉淀成页面`:
use `ask-and-file`, read the evidence first, then choose `output` for query-specific archives or `concept/entity/synthesis` for reusable knowledge
Important:
- honor explicit scope like `top 5`, `first 10`, `only AI-related`, `only raw/书评 1/`, or `do not modify yet`
- prefer small, high-confidence batches over giant rewrites
- for concept/entity/synthesis work, never create a derived page without first reading the supporting notes
## Chinese Intent Lexicon
Treat the following Chinese phrases as strong routing hints, even when the user does not mention tools or page types explicitly.
- `看一下`, `检查一下`, `盘一下`, `总览`, `扫一眼`, `先看看`:
default to inspection-first behavior with `kb_status` plus `kb_lint`
- `先别改`, `先不要修改`, `只看不改`, `先给我报告`:
do not call write tools; stay read-only unless the user later approves changes
- `补缺失`, `补 source`, `补书评`, `编译缺的`, `继续补 source note`:
treat this as `ingest-source` focused on `missing_source_note`
- `整理一下`, `清理一下`, `修一下占位内容`, `只修 AI 相关内容`, `把这部分弄干净`:
treat this as `maintain-wiki` with topic filters and placeholder cleanup
- `补概念页`, `补 entity`, `补 synthesis`, `沉淀成页面`, `把这个主题写成 page`:
treat this as derived-page work; search first, read evidence, then use `kb_map_gaps` or direct derived-note creation
- `修漂移`, `修 source id`, `修 manifest`, `先 dry run`:
route to `kb_repair_source_ids` with a dry run before any apply step
- `继续推进这份库`, `继续往前做`, `接着跑一轮`, `往前推进一批`:
treat this as an incremental maintenance batch, not as an open-ended rewrite
## Continuation Defaults
When the user says `继续推进这份库` or an equivalent continuation request without enough scope detail, run a conservative default batch:
1. call `kb_status`
2. call `kb_list_raw` with `changed_only=true` and an explicit `limit`
3. call `kb_lint`
4. if a dominant topic filter is obvious from the user request, run `kb_search` on that topic and read the top evidence notes
5. choose one primary batch only:
- compile a small batch of `missing_source_note` text raw files, or
- repair a small batch of placeholder-heavy source notes
6. optionally add one high-confidence `concept`, `entity`, or `synthesis` page if the evidence is already strong
7. call `kb_rebuild_indexes`
8. report what changed, what still looks weak, and the next best batch
Do not silently expand an underspecified continuation request into a large multi-hour rewrite.
## Scenario Presets
Use these presets when a short request clearly matches one of the common recurring workflows.
### `ai-topic-cleanup`
Strong triggers:
- `整理一下 AI 相关内容`
- `只修 AI 相关`
- `补 AI 概念页`
- `把 AI 这部分做扎实`
- `继续推进 AI 主题`
Default topic cluster:
- `AI`
- `AIGC`
- `ChatGPT`
- `prompt engineering`
- `AGI`
- `AI hype vs real value`
Default batch:
1. call `kb_lint`
2. run topic-focused `kb_search`
3. read the top evidence notes with `kb_read_notes`
4. repair a small batch of placeholder-heavy source notes first
5. if evidence is already strong, add one high-confidence `concept`, `entity`, or `synthesis` page
6. call `kb_rebuild_indexes`
Priority order:
- placeholder source titles
- placeholder open questions
- placeholder related links
- missing high-value concept/entity/synthesis pages
### `book-review-batch`
Strong triggers:
- `补书评`
- `继续编译书评`
- `把书评编进 wiki`
- `书评批处理`
- `补书评 source notes`
Default scope:
- only `raw/书评 1/`
- text-readable raw files only
- prioritize `missing_source_note`
Default batch:
1. call `kb_status`
2. call `kb_list_raw(changed_only=true)` with an explicit `limit`
3. filter to `raw/书评 1/`
4. compile the first 10 eligible text raw files through `kb_prepare_source`, `kb_read_raw`, and `kb_upsert_source_note`
5. call `kb_rebuild_indexes`
Writing expectations:
- human-facing title, never `元数据`
- real `Summary`, `Key Points`, `Evidence`, `Open Questions`, and `Related Links`
- grounded only in the raw content actually read
### `continue-this-library`
Strong triggers:
- `继续推进我的这份库`
- `接着跑一轮`
- `再往前推进一批`
- `今天继续做这个库`
Default decision rule:
- if `missing_source_note` backlog is obviously dominant, prefer a small ingest batch
- otherwise if placeholder-heavy source notes are clearly dominant, prefer a small cleanup batch
- otherwise read evidence and add one high-confidence derived page
Default batch cap:
- up to 10 source-note compiles, or
- up to 5 placeholder repairs, or
- 1 derived page
Do not mix all three into one large batch unless the user explicitly asks for a broader sweep.
## One-Line Shortcuts
These are valid compact requests.
Treat them as sufficient instructions unless the user adds more scope.
- `用 $llm-wiki-karpathy 检查一下我的 wiki,先别改。`
- `用 $llm-wiki-karpathy 补书评前 10 个。`
- `用 $llm-wiki-karpathy 整理一下 AI 相关内容。`
- `用 $llm-wiki-karpathy 补 3 个 concept pages。`
- `用 $llm-wiki-karpathy 修一下 source id 漂移,先 dry run。`
- `用 $llm-wiki-karpathy 继续推进我的这份库。`
## Canonical Actions
Treat the following as the four canonical high-level actions for this skill.
### `ingest-source`
Use this when the user wants to ingest, compile, or refresh changed raw material.
Sequence:
1. call `kb_status`
2. call `kb_list_raw` with `changed_only=true`
3. for each changed raw file:
- if the raw file is text or structured data:
- call `kb_prepare_source`
- call `kb_read_raw`
- compile the raw content into one grounded source note
- if the raw file is a PDF or image:
- call `kb_prepare_source_bundle`
- call `kb_get_raw_asset`
- if `compile_readiness` is not `ready`, create the missing representation trail with:
- `kb_prepare_representation`
- `kb_upsert_representation`
- call `kb_read_representations`
- compile the source note from the raw metadata plus the reviewed representations
- call `kb_upsert_source_note`
4. after the batch, call `kb_rebuild_indexes`
5. report what was compiled, what remains partial, and which raw assets still need representation work
Important:
- the actual LLM compile happens between the read/bundle steps and `kb_upsert_source_note`
- `kb_read_raw` is only for text-readable raw files
- PDFs usually become compile-ready once there is `native_text`, `ocr_text`, or `page_notes`
- images usually become compile-ready once there is `vision_notes`
- structured data is already text-readable, but `metadata` or `data_profile` can still improve later maintenance
- one raw file may later justify new `concept`, `entity`, or `synthesis` pages
- prefer incremental passes over giant rewrites
### `ask-and-file`
Use this when the user wants a grounded answer and the answer may deserve a durable artifact.
Sequence:
1. call `kb_search`
2. read only the most relevant notes with `kb_read_notes`
3. answer only from retrieved notes
4. decide the best write-back target:
- use `kb_prepare_output` + `kb_upsert_output` for question-specific answer archives
- use `kb_prepare_derived_note` + `kb_upsert_derived_note` when the answer should become a durable `concept`, `entity`, or `synthesis` page
5. call `kb_rebuild_indexes` if the wiki changed materially
Important:
- search before answering
- do not cite notes you did not read
- do not answer directly from non-text raw assets when the runtime already expects the grounded source note or stored representation trail
- choose `output` when preserving the exact query matters
- choose `concept/entity/synthesis` when the answer is reusable beyond the original query
### `maintain-wiki`
Use this when the user wants cleanup, organization, or a quality pass.
Sequence:
1. call `kb_lint`
2. inspect `wiki/index.md`, `wiki/log.md`, and the most relevant collection indexes with `kb_read_notes`
3. identify weak pages, missing derived pages, stale navigation, or grounding gaps
4. if source note ids, manifest entries, source note paths, or stored raw hashes have drifted, call `kb_repair_source_ids` first as a dry run and only apply it when the plan is correct
5. if the user wants fixes, repair narrowly through the appropriate `kb_*` write tools
6. call `kb_rebuild_indexes`
Important:
- prefer small targeted improvements
- keep source grounding visible
- use `concept/entity/synthesis` pages to absorb recurring structure instead of repeating the same reasoning in outputs
- treat `kb_lint` warnings as signals about wiki health, not only schema correctness
- pay special attention when `kb_lint` surfaces `missing_representation`, `representation_stale`, `unreviewed_asset_source`, stale source coverage, unresolved research gaps, unsupported claims, contradiction candidates, or missing high-value pages
- use `kb_repair_source_ids` for deterministic source-note/manifest repair instead of hand-editing ids, paths, or raw hashes
### `map-gaps`
Use this when the user wants to know what the wiki is missing.
Sequence:
1. call `kb_search`
2. read the relevant source, output, and derived pages
3. call `kb_map_gaps`
4. identify:
- repeated ideas that deserve a `concept` page
- repeated named items that deserve an `entity` page
- cross-source themes that deserve a `synthesis` page
5. if the user wants the page landed immediately, call `kb_promote_gap` with the candidate `note_id`
6. otherwise propose the best next pages in priority order
Important:
- prefer candidates with stronger `source_refs` coverage
- use `kb_promote_gap` when a current candidate should be landed as-is into the wiki
- use the returned draft template as the starting point when you want to refine the page before calling `kb_upsert_derived_note`
- treat the returned suggested opening and evidence summary as scaffolding, not final prose
## Writing Rules
### Source Notes
Required frontmatter fields:
- `id`
- `type: source`
- `title`
- `raw_path`
- `raw_hash`
- `source_kind`
- `tags`
- `created_at`
- `updated_at`
- `status`
Strongly recommended frontmatter fields:
- `raw_kind`
- `mime_type`
- `asset_paths`
Required headings:
- `# Summary`
- `# Key Points`
- `# Evidence`
- `# Open Questions`
- `# Related Links`
Multimodal guidance:
- for PDF and image source notes, `asset_paths` should include the primary reviewed raw asset
- add `# Visual Notes` when the note depends on multimodal review details that are not already obvious from the stored representation files
- do not claim a non-text asset was reviewed if neither the representation trail nor the visible review notes support that claim
### Output Notes
Required frontmatter fields:
- `id`
- `type: output`
- `title`
- `query`
- `source_refs`
- `created_at`
- `updated_at`
Required headings:
- `# Answer`
- `# Sources Used`
- `# Follow-up Questions`
### Derived Notes
Derived pages are for durable wiki structure, not ephemeral chat residue.
Supported kinds:
- `concept`
- `entity`
- `synthesis`
Shared frontmatter fields:
- `id`
- `type`
- `title`
- `aliases`
- `source_refs`
- `tags`
- `created_at`
- `updated_at`
- `status`
Required headings by kind:
- `concept`: `# Summary`, `# Definition`, `# Key Points`, `# Evidence`, `# Open Questions`, `# Related Notes`
- `entity`: `# Summary`, `# Who or What`, `# Key Facts`, `# Evidence`, `# Open Questions`, `# Related Notes`
- `synthesis`: `# Summary`, `# Thesis`, `# Supporting Evidence`, `# Tensions`, `# Open Questions`, `# Related Notes`
Guidance:
- `concept` pages capture reusable ideas or frames
- `entity` pages capture named things that recur
- `synthesis` pages capture higher-level cross-source conclusions that should survive beyond any one query
- `synthesis` pages capture multi-source analysis, tradeoffs, or contested views
- keep `source_refs` aligned with real source notes
## Safety Boundaries
- Never modify files under `raw/`.
- Never use generic file-writing tools to modify `wiki/` directly.
- Never invent note IDs, output IDs, or paths.
- Never claim a write succeeded unless the runtime confirms it.
- Never bypass the runtime after a tool failure.
## Failure Handling
If a runtime tool fails:
- explain the failure plainly
- keep the user context intact
- propose the next executable step
- do not work around the error by writing directly to the Vault
## Language Policy
- Write outward-facing artifacts in English by default.
- Preserve original-language titles or quotes only when they materially matter.
- If the user explicitly wants another language, follow that request for the final artifact.
## Finish Standard
When you finish a task with this skill, report:
- what was ingested, answered, maintained, or mapped
- which `kb_*` tools were used
- which files were created or updated
- any unresolved ambiguity or weak evidence
- the best next one to three follow-up steps
FILE:CHANGELOG.md
# Changelog
## 1.2.3 - 2026-04-13
- Rebrand the skill and plugin under the new llm-wiki-karpathy identity and refresh the publish workflow.
## 1.2.2 - 2026-04-06
- Add stronger natural-language routing and compact Chinese one-line prompts for common wiki workflows.
## 1.2.1 - 2026-04-05
- Document and ship deterministic repair for legacy src-untitled source ids.
## 1.2.0 - 2026-04-05
- Updated the skill docs for runtime `0.4.0` and its representation-first multimodal ingest model
- Added `kb_get_raw_asset`, `kb_prepare_source_bundle`, `kb_prepare_representation`, `kb_upsert_representation`, and `kb_read_representations` to the documented tool surface
- Added `kb_repair_source_ids` to the documented runtime and maintenance surface so source-id drift can be repaired deterministically
- Clarified the split between direct text/data ingest and PDF/image ingest that must go through stored representations first
- Documented multimodal grounding expectations around `raw_kind`, `mime_type`, `asset_paths`, visible review notes, and the new lint warnings for missing or stale representation trails
## 1.1.4 - 2026-04-05
- Added `kb_promote_gap` so a current gap candidate can be promoted straight into a real derived note
- Refactored the docs so map-gaps can either return refined drafts or land the best current candidate immediately
## 1.1.3 - 2026-04-05
- Upgraded `kb_map_gaps` again so each candidate now includes a suggested opening and evidence summary inside the draft payload
- Clarified in the docs that map-gaps outputs are meant to be refined into near-ready derived notes
## 1.1.2 - 2026-04-05
- Upgraded `kb_map_gaps` from a plain candidate report into a curation assistant with priorities and ready-to-fill Markdown drafts
- Clarified in the skill docs that map-gaps should feed directly into `kb_upsert_derived_note`
## 1.1.1 - 2026-04-05
- Added `kb_map_gaps` to the documented runtime surface
- Updated the skill and agent prompts to treat gap mapping as a first-class workflow
- Clarified how the wiki should identify the next missing concept, entity, and synthesis pages
## 1.1.0 - 2026-04-05
- Repositioned the skill around a wiki-first operating model instead of a runtime-first three-action story
- Added first-class support in the docs and agent prompts for `concept`, `entity`, and `synthesis` pages
- Expanded the recommended vault layout to include `wiki/concepts/`, `wiki/entities/`, `wiki/syntheses/`, `wiki/index.md`, and `wiki/log.md`
- Reframed the canonical workflows as `ingest-source`, `ask-and-file`, `maintain-wiki`, and `map-gaps`
- Updated the scaffold script to create the new wiki directories and generated navigation placeholders
## 1.0.9 - 2026-04-05
- Repositioned the skill around the standalone CLI/MCP runtime instead of describing the product as OpenClaw-first
- Updated README, release notes, and operator prompts to treat OpenClaw as one compatible host alongside Claude Code, Codex, Cursor, and Gemini CLI
- Bumped ClawHub publish metadata to match the new standalone runtime story
## 1.0.8 - 2026-04-04
- Added a Claude Code MCP install path so the same `kb_*` contract can be used outside the OpenClaw host
- Added a reusable Claude Code subagent prompt that carries over the skill's compile, ask, and lint workflow rules
- Added a Codex `AGENTS.md` template plus a portable operator guide for other MCP-capable agents
- Added a multi-agent config helper path for Codex, Cursor, and Gemini CLI
- Updated the README and package metadata to document the cross-agent installation path
FILE:README.md
# LLM Wiki Karpathy
Inspired by a public workflow shared by Andrej Karpathy (@karpathy).
From raw text, PDFs, images, and structured data to a living Markdown wiki that compounds with every question.
## Product Model
`LLM Wiki Karpathy` is best understood as a local-first wiki operating system for research.
The core model is:
- `raw/` holds outside-world source material
- `wiki/` holds the persistent knowledge layer the agent maintains
- `schema` defines what kinds of pages belong in the wiki and how they relate
- every ingest, query, and maintenance pass should improve the wiki itself
The runtime provides deterministic guardrails for paths, IDs, validation, and writes.
The skill tells the agent how to grow the wiki into something durable and navigable.
## What 1.2.1 Changes
The old mental model was mostly text-first:
- compile raw notes into `wiki/sources/`
- archive answers into `wiki/outputs/`
- lint the structure
The upgraded multimodal model is:
- ingest text and structured data directly into source pages
- inspect PDFs and images through deterministic raw asset metadata
- store OCR, vision, page-note, metadata, and profiling artifacts under `.llm-kb/representations/`
- compile non-text source pages from the full source bundle instead of pretending `kb_read_raw` can read binary inputs
- repair stale legacy `src-untitled-*` source ids deterministically before maintenance or compile flows keep spreading them
- promote recurring ideas into `concept` and `entity` pages
- write cross-source analysis into `synthesis` pages
- keep `wiki/index.md` as a master catalog, `wiki/log.md` as a readable activity log, and `_indexes/` current
- let important answers write back into the wiki instead of disappearing into chat history
This is closer to the original Karpathy-style wiki workflow: the wiki is the product, not just a side effect of a runtime.
## Supported Raw Inputs
The runtime now recognizes:
- text: `.md`, `.txt`
- PDFs: `.pdf`
- images: `.png`, `.jpg`, `.jpeg`, `.webp`, `.gif`, `.svg`
- structured data: `.csv`, `.tsv`, `.json`, `.html`
## Current Page Types
Runtime-backed page kinds now include:
- `source` for grounded source notes compiled from `raw/`
- `output` for archived answers tied to a specific question
- `concept` for reusable ideas that recur across notes
- `entity` for named systems, people, products, orgs, methods, or datasets
- `synthesis` for higher-level cross-source thinking
- `index` for generated navigation pages
- `log` for the generated run log page
## Default Repository Shape
```text
<vault>/
raw/
inbox/
web/
notes/
papers/
repos/
datasets/
images/
wiki/
sources/
outputs/
concepts/
entities/
syntheses/
_indexes/
index.md
log.md
.llm-kb/
manifest.json
runs.jsonl
representations/
```
## High-Level Actions
The skill should think in terms of four high-level actions:
- `ingest-source`: compile changed raw files into `source` pages, using the direct path for text/data and the representation-first path for PDFs/images
- `ask-and-file`: answer from retrieved notes and archive the result as an `output` or promote it into a richer wiki page
- `maintain-wiki`: improve navigation, derived pages, consistency, wiki-health signals, and repair stale source ids or manifest/source-note drift when needed
- `map-gaps`: identify missing concept/entity/synthesis pages, produce prioritized draft templates, and optionally promote the best current candidate straight into a real derived page
## Runtime-Backed Tools
The runtime now supports the core wiki-maintenance surface:
- `kb_status`
- `kb_list_raw`
- `kb_read_raw`
- `kb_get_raw_asset`
- `kb_prepare_source`
- `kb_prepare_source_bundle`
- `kb_prepare_representation`
- `kb_upsert_representation`
- `kb_read_representations`
- `kb_upsert_source_note`
- `kb_prepare_output`
- `kb_upsert_output`
- `kb_prepare_derived_note`
- `kb_upsert_derived_note`
- `kb_search`
- `kb_read_notes`
- `kb_map_gaps`
- `kb_promote_gap`
- `kb_repair_source_ids`
- `kb_rebuild_indexes`
- `kb_lint`
## Install
```bash
clawhub install llm-wiki-karpathy
```
For Claude Code:
```bash
claude mcp add llm-wiki-karpathy -- \
npx -y --package @harrylabs/llm-wiki-karpathy@latest \
llm-wiki-karpathy-mcp \
--vault-root /absolute/path/to/your/obsidian-vault
```
For Codex, copy [`agents/codex-AGENTS.md`](agents/codex-AGENTS.md) into your project root as `AGENTS.md`.
For other MCP-capable agents:
```bash
npx -y --package @harrylabs/llm-wiki-karpathy@latest \
llm-wiki-karpathy-configs --vault-root /absolute/path/to/your/obsidian-vault
```
To scaffold a new vault:
```bash
bash scripts/init_llm_kb_repo.sh my-knowledge-base
```
## Example Prompts
Users do not need to name canonical actions.
Short natural-language requests should still map onto the runtime workflow cleanly.
The explicit prefix `$llm-wiki-karpathy` is optional; use it when you want the clearest possible routing.
```text
Use $llm-wiki-karpathy to ingest changed text notes and refresh the wiki indexes.
```
```text
Use $llm-wiki-karpathy to inspect changed PDFs, store any missing representations, compile grounded source pages, and refresh the indexes.
```
```text
Use $llm-wiki-karpathy to answer "What are the tradeoffs between retrieval and memory finetuning?" and file the result back into the wiki.
```
```text
Use $llm-wiki-karpathy to inspect the wiki for missing concept or entity pages around agent memory systems.
```
```text
Use $llm-wiki-karpathy to run map-gaps and tell me the next five pages this wiki should gain.
```
```text
Use $llm-wiki-karpathy to run map-gaps and immediately promote the top synthesis candidate into the wiki.
```
```text
Use $llm-wiki-karpathy to run a wiki maintenance pass and tell me which pages are stale, missing, or weakly grounded.
```
```text
Use $llm-wiki-karpathy to check my wiki, run kb_status and kb_lint, tell me the current counts, the top 5 issues, and the single best next action. Do not modify anything yet.
```
```text
Use $llm-wiki-karpathy to fill missing source notes under raw/书评 1/. Run kb_list_raw with changed_only=true and limit=200, prioritize missing_source_note, compile the first 10 text raw files, rebuild indexes, and tell me what remains missing.
```
```text
Use $llm-wiki-karpathy to clean up AI-related pages. Focus on AI, AIGC, ChatGPT, prompt engineering, and AGI; fix placeholder titles, open questions, and related links on 5 notes, then rebuild indexes.
```
```text
Use $llm-wiki-karpathy to add concept pages around agent memory. Search first, read the supporting notes, run kb_map_gaps, land the top 3 concept pages, and rebuild indexes.
```
```text
Use $llm-wiki-karpathy to repair source-id and manifest drift. Dry-run kb_repair_source_ids first, explain the repair plan, apply it if the plan is sound, then rebuild indexes and report the repair counts.
```
```text
Use $llm-wiki-karpathy to answer "What recurring judgments show up across my AI-related book reviews?" Read only the most relevant notes first, then write the durable result back as a concept or synthesis page if it is reusable.
```
## Chinese Compact Prompts
```text
用 $llm-wiki-karpathy 检查一下我的 wiki,先别改,告诉我当前数量、最重要的 5 个问题、以及最值得做的下一步。
```
```text
用 $llm-wiki-karpathy 补一下缺失的 source notes,只处理 raw/书评 1/,先做前 10 个,再刷新索引。
```
```text
用 $llm-wiki-karpathy 整理一下 AI 相关页面,优先修标题像占位词、Open Questions 还是待整理、Related Links 还是待补充的 source notes。
```
```text
用 $llm-wiki-karpathy 补 3 个最值得做的 concept pages,先搜索和读证据,再落地页面。
```
```text
用 $llm-wiki-karpathy 继续推进这份库:保守地跑一轮,小批量补缺失、修占位页,并在证据足够时补 1 个 derived page。
```
## Scenario Shortcuts
```text
用 $llm-wiki-karpathy 整理一下 AI 相关内容。
```
```text
用 $llm-wiki-karpathy 补书评前 10 个。
```
```text
用 $llm-wiki-karpathy 继续推进我的这份库。
```
## One-Line Chinese Pack
直接复制下面任意一句就能用:
```text
用 $llm-wiki-karpathy 检查一下我的 wiki,先别改。
```
```text
用 $llm-wiki-karpathy 补书评前 10 个。
```
```text
用 $llm-wiki-karpathy 整理一下 AI 相关内容。
```
```text
用 $llm-wiki-karpathy 补 3 个 concept pages。
```
```text
用 $llm-wiki-karpathy 修一下 source id 漂移,先 dry run。
```
```text
用 $llm-wiki-karpathy 继续推进我的这份库。
```
如果上下文已经明确是在操作这个知识库,通常也可以直接省略 `$llm-wiki-karpathy`,例如:
```text
检查一下我的 wiki,先别改。
```
```text
补书评前 10 个。
```
```text
整理一下 AI 相关内容。
```
## Daily 3-Pack For This Vault
如果只保留 3 句,优先保留这 3 句:
```text
用 $llm-wiki-karpathy 检查一下我的 wiki,先别改,告诉我当前数量、最重要的 5 个问题、以及今天最值得做的一步。
```
```text
用 $llm-wiki-karpathy 继续推进我的这份库:只处理 raw/书评 1/,优先补前 10 个 missing source notes,然后刷新索引。
```
```text
用 $llm-wiki-karpathy 整理一下 AI 相关内容:先修一小批占位感强的 source notes,再补 1 个最值得做的 concept 或 synthesis page。
```
## Writing Style
This skill prefers durable wiki artifacts over chat-only answers:
- source-grounded pages
- explicit multimodal review trails through `raw_kind`, `mime_type`, `asset_paths`, and `# Visual Notes` when needed
- explicit `source_refs`
- linked Markdown pages that humans can browse
- generated indexes and logs that keep the vault navigable
- warnings from `kb_lint` that highlight missing or stale representations, inconsistent `asset_paths`, isolated draft pages, missing cross-links, stale source coverage, unresolved research gaps, unsupported claims, contradiction candidates, placeholder content, and medium/high-value missing pages before those problems spread
- `kb_repair_source_ids` when source note ids, paths, manifest entries, or stored raw hashes have drifted and need a deterministic repair pass
Outward-facing artifacts default to English unless the user explicitly asks otherwise.
FILE:RELEASE.md
# LLM Wiki Karpathy Release Notes
## Short Description
Inspired by a public workflow shared by Andrej Karpathy (@karpathy).
From raw text, PDFs, images, and structured data to a living Markdown wiki that compounds with every question.
## Marketplace Card Copy
Title:
- LLM Wiki Karpathy
Slug:
- llm-wiki-karpathy
Short description:
- Inspired by a public workflow shared by Andrej Karpathy (@karpathy).
Install hook:
- From raw text, PDFs, images, and structured data to a living Markdown wiki that compounds with every question
## Announcement Copy
`LLM Wiki Karpathy` skill `1.2.3` now ships alongside runtime `0.4.4` and republishes the workflow under a cleaner public identity:
- the skill slug is now `llm-wiki-karpathy`
- the npm runtime is now `@harrylabs/llm-wiki-karpathy`
- the ClawHub code plugin is now `llm-wiki-karpathy-plugin`
- legacy `llm-knowledge-bases` and `llm-kb` command aliases are still available during the transition
The workflow remains local-first and Markdown-first. This release is mostly about shipping the same wiki workflow under a tighter, Karpathy-attributed name while keeping the underlying `kb_*` contract intact.
This release is still explicitly inspired by a public workflow shared by Andrej Karpathy ([@karpathy](https://x.com/karpathy)) around using LLMs to maintain personal knowledge bases built from Markdown, images, and accumulated outputs. That attribution helps position the skill in a recognizable lineage without implying endorsement.
The current release focuses on:
- renaming the skill, runtime package, code plugin, and publish scripts to `llm-wiki-karpathy`
- preserving the existing `kb_*` runtime contract and transition aliases for the old command names
- refreshing release metadata so npm, ClawHub skill publishing, and ClawHub code-plugin publishing all point at the new identity
It works especially well with Obsidian, while staying portable because everything remains plain Markdown.
## Suggested Tags
- knowledge-base
- research
- markdown
- wiki
- obsidian
- multimodal
- pdf
- image
- data
- local-first
## Suggested Repo Name
- `openclaw-skill-llm-wiki-karpathy`
## Publish Command
```bash
clawhub publish /absolute/path/to/llm-wiki-karpathy \
--slug llm-wiki-karpathy \
--name "LLM Wiki Karpathy" \
--version "1.2.3" \
--changelog "Rebrand the skill and plugin under the new llm-wiki-karpathy identity and refresh the publish workflow." \
--tags "knowledge-base,research,markdown,wiki,obsidian,multimodal,pdf,image,data,local-first"
```
FILE:agents/claude-code-subagent.md
---
name: llm-wiki-karpathy
description: Use this subagent when the user wants to ingest text, PDF, image, or structured-data raw material into the wiki, answer from the knowledge base with file-back support, maintain concept/entity/synthesis pages, or run deterministic multimodal wiki lint.
---
You operate a Vault that is managed by the `LLM Wiki Karpathy` MCP server.
Operating model:
- `raw/` stores captured source material
- `wiki/sources/` stores compiled source pages
- `wiki/outputs/` stores archived answers
- `wiki/concepts/`, `wiki/entities/`, and `wiki/syntheses/` store durable derived pages
- `wiki/_indexes/`, `wiki/index.md`, and `wiki/log.md` store generated navigation
- `.llm-kb/` stores runtime state
- `.llm-kb/representations/` stores runtime-managed OCR, vision, metadata, and profiling artifacts for non-text assets
Boundaries:
- use MCP tools for all Vault reads and writes
- do not modify `raw/`
- do not write directly into `wiki/` or `.llm-kb/representations/` with generic file tools
- do not invent IDs, paths, hashes, representation paths, or `source_refs`
- use `kb_read_raw` only for text-readable raw files
- use the representation-first path for PDFs and images
Required MCP tools:
- `kb_status`
- `kb_list_raw`
- `kb_read_raw`
- `kb_get_raw_asset`
- `kb_prepare_source`
- `kb_prepare_source_bundle`
- `kb_prepare_representation`
- `kb_upsert_representation`
- `kb_read_representations`
- `kb_upsert_source_note`
- `kb_prepare_output`
- `kb_upsert_output`
- `kb_prepare_derived_note`
- `kb_upsert_derived_note`
- `kb_search`
- `kb_read_notes`
- `kb_map_gaps`
- `kb_promote_gap`
- `kb_repair_source_ids`
- `kb_rebuild_indexes`
- `kb_lint`
Preferred actions:
1. `ingest-source`
- text/data: `kb_prepare_source` -> `kb_read_raw`
- PDFs/images: `kb_prepare_source_bundle` -> `kb_get_raw_asset` -> representation tools -> `kb_read_representations`
2. `ask-and-file`
3. `maintain-wiki`
- use `kb_lint`, then `kb_repair_source_ids` when source ids, manifest entries, source note paths, or raw hashes have drifted
4. `map-gaps`
- `kb_search`
- `kb_read_notes`
- `kb_map_gaps`
- optionally `kb_promote_gap` to land a current candidate immediately
Use `output` notes for query-specific archives.
Use `concept`, `entity`, and `synthesis` notes when the result should become durable wiki structure.
For PDF/image source notes, keep `asset_paths` aligned with the reviewed assets and add `Visual Notes` when the source depends on multimodal review details that are not already captured by stored representations.
FILE:agents/codex-AGENTS.md
# LLM Wiki Karpathy
Use this guide when Codex should operate a Vault managed by the `LLM Wiki Karpathy` MCP server.
Core model:
- `raw/` stores captured source material
- `wiki/` stores the persistent knowledge layer
- `wiki/sources/` stores compiled source pages
- `wiki/outputs/` stores archived answers
- `wiki/concepts/`, `wiki/entities/`, and `wiki/syntheses/` store durable derived pages
- `wiki/_indexes/`, `wiki/index.md`, and `wiki/log.md` keep the vault navigable
- `.llm-kb/representations/` stores runtime-managed OCR, vision, metadata, and profiling artifacts for non-text assets
Boundaries:
- treat the Vault as runtime-managed
- use MCP tools for all Vault reads and writes
- never modify `raw/`
- never write directly into `wiki/` or `.llm-kb/representations/` with generic file tools
- never invent IDs, hashes, representation paths, or `source_refs`
- use `kb_read_raw` only for text-readable raw files
- use the representation-first path for PDFs and images
Required MCP tools:
- `kb_status`
- `kb_list_raw`
- `kb_read_raw`
- `kb_get_raw_asset`
- `kb_prepare_source`
- `kb_prepare_source_bundle`
- `kb_prepare_representation`
- `kb_upsert_representation`
- `kb_read_representations`
- `kb_upsert_source_note`
- `kb_prepare_output`
- `kb_upsert_output`
- `kb_prepare_derived_note`
- `kb_upsert_derived_note`
- `kb_search`
- `kb_read_notes`
- `kb_map_gaps`
- `kb_promote_gap`
- `kb_repair_source_ids`
- `kb_rebuild_indexes`
- `kb_lint`
Canonical actions:
1. `ingest-source`
- `kb_status`
- `kb_list_raw(changed_only=true)`
- for text/data: `kb_prepare_source` -> `kb_read_raw`
- for PDFs/images: `kb_prepare_source_bundle` -> `kb_get_raw_asset` -> representation tools -> `kb_read_representations`
- write one grounded source page
- `kb_upsert_source_note`
- `kb_rebuild_indexes`
2. `ask-and-file`
- `kb_search`
- `kb_read_notes`
- answer only from retrieved notes
- use `kb_upsert_output` for query-specific archives
- use `kb_upsert_derived_note` for durable concept/entity/synthesis pages
3. `maintain-wiki`
- `kb_lint`
- inspect indexes and relevant pages
- use `kb_repair_source_ids` first when source ids, manifest entries, source note paths, or raw hashes have drifted
- repair narrowly through runtime tools
- `kb_rebuild_indexes`
4. `map-gaps`
- `kb_search`
- `kb_read_notes`
- `kb_map_gaps`
- optionally `kb_promote_gap` when the best current candidate should be landed immediately
Natural-language triggers:
The explicit prefix `$llm-wiki-karpathy` is optional.
Use it when you want maximum routing certainty, but short natural-language requests should still trigger this guide when the intent is clear.
- `check my wiki`, `总览检查`: run `kb_status` plus `kb_lint`, report counts, top issues, and the best next action; do not write unless asked
- `fill missing source notes`, `继续推进这份库`: use `kb_status` plus `kb_list_raw(changed_only=true)` with an explicit `limit`, prioritize `missing_source_note`, compile in small batches, then rebuild indexes
- `clean up these pages`, `fix placeholder titles`, `做一次维护清理`: start with `kb_lint`, then use `kb_search` plus `kb_read_notes` to target placeholder titles, open questions, related links, stale navigation, and other high-value health issues
- `add concept/entity/synthesis pages`, `what pages are missing?`: search first, read evidence, then run `kb_map_gaps`; use `kb_promote_gap` when the candidate can be landed directly
- `repair source ids`, `repair manifest drift`: run `kb_repair_source_ids` as a dry run first, explain the plan, then apply only if it looks correct
- `answer this from the wiki and save it back`: use `ask-and-file`; prefer `concept/entity/synthesis` over `output` when the result is reusable beyond the current query
Chinese routing hints:
- `看一下`, `检查一下`, `盘一下`, `总览`, `先看看`: inspection-first, usually `kb_status` plus `kb_lint`
- `先别改`, `只看不改`, `先给我报告`: read-only mode unless the user later asks for writes
- `补缺失`, `补书评`, `编译缺的 source`: `ingest-source` focused on `missing_source_note`
- `整理一下`, `清理一下`, `修一下占位内容`: `maintain-wiki` with placeholder cleanup and topic filters
- `补概念页`, `补 entity`, `补 synthesis`, `沉淀成页面`: derived-page flow; always read evidence before writing
- `修漂移`, `修 source id`, `修 manifest`, `先 dry run`: `kb_repair_source_ids` dry run first
- `继续推进这份库`, `接着跑一轮`: conservative continuation batch, not a giant rewrite
Continuation default when the request is underspecified:
1. `kb_status`
2. `kb_list_raw(changed_only=true)` with an explicit `limit`
3. `kb_lint`
4. optional topic-targeted `kb_search` plus `kb_read_notes`
5. choose one primary batch: either a small `missing_source_note` compile batch or a small placeholder-repair batch
6. optionally add one high-confidence derived page
7. `kb_rebuild_indexes`
Scenario presets:
- `整理一下 AI 相关内容`, `只修 AI 相关`, `补 AI 概念页`
run `kb_lint`, then topic-focused `kb_search` plus `kb_read_notes`; repair a small batch of placeholder-heavy notes first, then optionally land one high-confidence derived page
- `补书评`, `继续编译书评`, `书评批处理`
scope to `raw/书评 1/` text raw files, prioritize `missing_source_note`, compile up to 10 notes, then rebuild indexes
- `继续推进我的这份库`, `接着跑一轮`
choose one primary batch only: either up to 10 source-note compiles, up to 5 placeholder repairs, or 1 derived page
When `继续推进这份库` is underspecified, prefer whichever of these is most clearly dominant in `kb_status` plus `kb_lint`.
One-line shortcuts that should be treated as complete enough requests:
- `用 $llm-wiki-karpathy 检查一下我的 wiki,先别改`
- `用 $llm-wiki-karpathy 补书评前 10 个`
- `用 $llm-wiki-karpathy 整理一下 AI 相关内容`
- `用 $llm-wiki-karpathy 补 3 个 concept pages`
- `用 $llm-wiki-karpathy 修一下 source id 漂移,先 dry run`
- `用 $llm-wiki-karpathy 继续推进我的这份库`
The same one-line requests should also work without the `$llm-wiki-karpathy` prefix when the surrounding context is clearly about this wiki.
Always honor explicit scope like `top 5`, `first 10`, `only AI-related`, `only raw/书评 1/`, or `do not modify yet`.
Writing rules:
- source pages need `Summary`, `Key Points`, `Evidence`, `Open Questions`, `Related Links`
- multimodal source pages should keep `raw_kind`, `mime_type`, and `asset_paths` aligned with the reviewed asset trail
- PDF/image source pages should usually include `Visual Notes` when the review evidence is not already obvious from stored representations
- output pages need `Answer`, `Sources Used`, `Follow-up Questions`
- concept pages need `Summary`, `Definition`, `Key Points`, `Evidence`, `Open Questions`, `Related Notes`
- entity pages need `Summary`, `Who or What`, `Key Facts`, `Evidence`, `Open Questions`, `Related Notes`
- synthesis pages need `Summary`, `Thesis`, `Supporting Evidence`, `Tensions`, `Open Questions`, `Related Notes`
Finish by stating:
- what was ingested, answered, or maintained
- which MCP tools were used
- which pages were created or updated
- any unresolved ambiguity or weak evidence
FILE:agents/openai.yaml
interface:
display_name: "LLM Wiki Karpathy"
short_description: "From raw text, PDFs, images, and data to a compounding Markdown wiki"
default_prompt: "Use this skill through one-line requests like '检查一下我的 wiki,先别改', '补书评前 10 个', '整理一下 AI 相关内容', '补 3 个 concept pages', '修一下 source id 漂移,先 dry run', or '继续推进我的这份库'. Adding `$llm-wiki-karpathy` is optional but gives the most explicit routing."
FILE:agents/portable-operator.md
# Portable Operator Instructions
Use these instructions for any MCP-capable agent that should operate an `LLM Wiki Karpathy` vault.
Core model:
- the runtime owns Vault I/O, IDs, paths, validation, state, and generated navigation
- the agent owns understanding, synthesis, Q&A, and deciding which wiki pages should exist
- every meaningful interaction should improve the wiki, not only produce a chat answer
Preferred actions:
- `ingest-source`
- `ask-and-file`
- `maintain-wiki`
- `map-gaps`
Non-negotiable boundaries:
- never modify files under `raw/`
- never write directly into `wiki/` with generic file tools
- never fabricate note ids, hashes, or `source_refs`
- never claim a write succeeded unless the MCP tool confirmed it
Preferred tool order:
- ingest (text/data): `kb_status` -> `kb_list_raw` -> `kb_prepare_source` -> `kb_read_raw` -> `kb_upsert_source_note` -> `kb_rebuild_indexes`
- ingest (pdf/image): `kb_status` -> `kb_list_raw` -> `kb_prepare_source_bundle` -> `kb_get_raw_asset` -> `kb_prepare_representation` -> `kb_upsert_representation` -> `kb_read_representations` -> `kb_upsert_source_note` -> `kb_rebuild_indexes`
- answer: `kb_search` -> `kb_read_notes` -> answer -> `kb_upsert_output` or `kb_upsert_derived_note`
- maintain: `kb_lint` -> `kb_read_notes` -> optional `kb_repair_source_ids` when source ids or manifest/source-note metadata drifted -> targeted fixes -> `kb_rebuild_indexes`
- gap mapping: `kb_search` -> `kb_read_notes` -> `kb_map_gaps` -> optional `kb_promote_gap`
FILE:clawhub.json
{
"name": "llm-wiki-karpathy",
"version": "1.2.3",
"description": "Inspired by a public workflow shared by Andrej Karpathy (@karpathy). From raw text, PDFs, images, and structured data to a living Markdown wiki that compounds with every question.",
"keywords": [
"llm-wiki-karpathy",
"knowledge-base",
"research",
"markdown",
"wiki",
"obsidian",
"multimodal",
"pdf",
"image",
"data",
"local-first"
]
}
FILE:references/maintenance-playbook.md
# Maintenance Playbook
Use this playbook when the wiki has grown enough that structure drift starts to hurt trust or navigation.
## When To Run A Maintenance Pass
Run a pass when:
- a large batch of raw sources landed
- the wiki is answering well but feels hard to navigate
- the same ideas keep appearing in outputs without dedicated pages
- users report stale, missing, or contradictory notes
- `wiki/index.md` or collection indexes no longer help orient the corpus
## Core Checks
### 1. Source Coverage
Check whether new files in `raw/` are missing corresponding pages in `wiki/sources/`.
Look for:
- unprocessed raw items
- raw files with no manifest record
- raw files with no source page
- source pages that are too shallow to support reuse
### 2. Derived Page Gaps
Check whether the wiki is missing obvious durable pages.
Look for:
- repeated ideas that deserve a `concept` page
- repeated named items that deserve an `entity` page
- repeated cross-source tradeoff or comparison work that deserves a `synthesis` page
### 3. Grounding Quality
Check whether pages remain source-grounded.
Look for:
- weak `Evidence` sections
- `source_refs` that do not correspond to real source pages
- outputs that overstate confidence
- derived pages that sound plausible but cannot be traced back to sources
### 4. Navigation Quality
Check whether generated and human-facing navigation still works.
Look for:
- stale `wiki/index.md`
- missing collection indexes
- pages with no meaningful `Related Notes`
- large clusters of pages that are hard to discover from search or indexes
### 5. Promotion Quality
Check whether the wiki is still treating outputs appropriately.
Look for:
- valuable answers trapped only in `wiki/outputs/`
- recurring question-specific notes that should become durable wiki pages
- concepts or entities repeatedly recreated in multiple outputs
## Cleanup Strategy
Prefer small targeted passes over dramatic rewrites.
Recommended order:
1. run `kb_lint`
2. restore missing source pages and missing generated indexes
3. repair broken `source_refs` or bad canonical paths
4. promote the highest-value repeated ideas into `concept/entity/synthesis` pages
5. rebuild indexes and re-check search quality
## Improvement Ideas
Once the basics are healthy, useful next upgrades include:
- backlink-aware orphan reports
- contradiction or tension candidate reports
- stale-page reports based on update history
- stronger prompts for promoting outputs into durable pages
- workflow wrappers for wiki curation, not only raw-source compilation
Only build tools that remove repeated wiki-maintenance work for the agent.
## Deliverable Format
When reporting a maintenance pass, include:
- the highest-signal issues found
- the pages created or updated
- which problems remain unresolved
- the next one to three highest-value pages or fixes
FILE:references/repo-layout.md
# Repository Layout
This skill works best when the knowledge base stays easy to browse for both humans and agents, and when the runtime owns the canonical write boundaries.
## Default Layout
```text
<vault>/
raw/
inbox/
web/
notes/
papers/
repos/
datasets/
images/
wiki/
sources/
outputs/
concepts/
entities/
syntheses/
_indexes/
index.md
log.md
.llm-kb/
manifest.json
runs.jsonl
```
## Folder Contract
- `raw/`: source-of-truth artifacts captured from the outside world. Preserve filenames, provenance, and local assets. Runtime support is currently centered on `.md` and `.txt` raw files.
- `wiki/sources/`: one source page per raw source, written only through the runtime.
- `wiki/outputs/`: archived answers that preserve the original question context.
- `wiki/concepts/`: durable concept pages for reusable ideas or frameworks.
- `wiki/entities/`: durable pages for named systems, people, organizations, tools, methods, or datasets.
- `wiki/syntheses/`: cross-source pages for tradeoffs, comparisons, theses, and higher-order analysis.
- `wiki/_indexes/`: generated collection indexes such as `sources.md`, `outputs.md`, `concepts.md`, `entities.md`, and `syntheses.md`.
- `wiki/index.md`: generated wiki home page.
- `wiki/log.md`: generated run log page.
- `.llm-kb/manifest.json`: canonical mapping of raw files to source notes and hashes.
- `.llm-kb/runs.jsonl`: append-only run log for audit and generated `wiki/log.md`.
## Recommended Page Shapes
### Source Page
Store source pages in `wiki/sources/` with the runtime-issued `doc_id`.
Required headings:
- `Summary`
- `Key Points`
- `Evidence`
- `Open Questions`
- `Related Links`
Required frontmatter:
- `id`
- `type: source`
- `title`
- `raw_path`
- `raw_hash`
- `source_kind`
- `tags`
- `created_at`
- `updated_at`
- `status`
### Output Page
Store question-specific answer archives in `wiki/outputs/`.
Required headings:
- `Answer`
- `Sources Used`
- `Follow-up Questions`
Required frontmatter:
- `id`
- `type: output`
- `title`
- `query`
- `source_refs`
- `created_at`
- `updated_at`
### Concept Page
Use `wiki/concepts/` for reusable ideas that recur across source notes and answers.
Required headings:
- `Summary`
- `Definition`
- `Key Points`
- `Evidence`
- `Open Questions`
- `Related Notes`
### Entity Page
Use `wiki/entities/` for named things that deserve durable context.
Required headings:
- `Summary`
- `Who or What`
- `Key Facts`
- `Evidence`
- `Open Questions`
- `Related Notes`
### Synthesis Page
Use `wiki/syntheses/` for cross-source analysis and tradeoff pages.
Required headings:
- `Summary`
- `Thesis`
- `Supporting Evidence`
- `Tensions`
- `Open Questions`
- `Related Notes`
### Derived Frontmatter
For `concept`, `entity`, and `synthesis`, include:
- `id`
- `type`
- `title`
- `aliases`
- `source_refs`
- `tags`
- `created_at`
- `updated_at`
- `status`
## Naming
- source notes: `wiki/sources/src-<slug>.md`
- outputs: `wiki/outputs/YYYY-MM-DD-<slug>.md`
- concepts: `wiki/concepts/concept-<slug>.md`
- entities: `wiki/entities/entity-<slug>.md`
- syntheses: `wiki/syntheses/synthesis-<slug>.md`
## Growth Rule
Start with these page kinds and let the wiki earn more structure gradually.
Good growth:
- promote repeated ideas into `concept` pages
- promote repeated named items into `entity` pages
- promote recurring cross-source reasoning into `synthesis` pages
Bad growth:
- inventing a huge taxonomy before the wiki needs it
- creating many directories with no durable workflow behind them
- archiving every thought as an `output` when it should really become a reusable wiki page
FILE:scripts/init_llm_kb_repo.sh
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 1 ]]; then
echo "Usage: bash scripts/init_llm_kb_repo.sh <target-dir>" >&2
exit 1
fi
target_dir="$1"
mkdir -p \
"$target_dir/raw/inbox" \
"$target_dir/raw/web" \
"$target_dir/raw/notes" \
"$target_dir/raw/papers" \
"$target_dir/raw/repos" \
"$target_dir/raw/datasets" \
"$target_dir/raw/images" \
"$target_dir/wiki/sources" \
"$target_dir/wiki/outputs" \
"$target_dir/wiki/concepts" \
"$target_dir/wiki/entities" \
"$target_dir/wiki/syntheses" \
"$target_dir/wiki/_indexes" \
"$target_dir/.llm-kb"
write_if_missing() {
local path="$1"
local content="$2"
if [[ -e "$path" ]]; then
return 0
fi
printf '%s\n' "$content" > "$path"
}
write_if_missing "$target_dir/wiki/_indexes/sources.md" "# Sources Index
Generated by the runtime. Rebuild with \`kb_rebuild_indexes\` after compiling source notes."
write_if_missing "$target_dir/wiki/_indexes/outputs.md" "# Outputs Index
Generated by the runtime. Rebuild with \`kb_rebuild_indexes\` after archiving output notes."
write_if_missing "$target_dir/wiki/_indexes/concepts.md" "# Concepts Index
Generated by the runtime. Rebuild with \`kb_rebuild_indexes\` after writing concept notes."
write_if_missing "$target_dir/wiki/_indexes/entities.md" "# Entities Index
Generated by the runtime. Rebuild with \`kb_rebuild_indexes\` after writing entity notes."
write_if_missing "$target_dir/wiki/_indexes/syntheses.md" "# Syntheses Index
Generated by the runtime. Rebuild with \`kb_rebuild_indexes\` after writing synthesis notes."
write_if_missing "$target_dir/wiki/index.md" "# Knowledge Base Index
Generated by the runtime. Rebuild with \`kb_rebuild_indexes\` after changing the wiki."
write_if_missing "$target_dir/wiki/log.md" "# Knowledge Base Log
Generated by the runtime from \`.llm-kb/runs.jsonl\`."
write_if_missing "$target_dir/.llm-kb/manifest.json" '{
"schema_version": 1,
"vault_root": "REPLACE_WITH_ABSOLUTE_VAULT_PATH",
"sources": {}
}'
write_if_missing "$target_dir/.llm-kb/runs.jsonl" ""
echo "Initialized knowledge base scaffold at: $target_dir"
FILE:scripts/publish.sh
#!/usr/bin/env bash
set -euo pipefail
skill_dir="$(cd "$(dirname "$0")/.." && pwd)"
version="$(node -e 'const fs=require("node:fs"); const pkg=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); console.log(pkg.version);' "$skill_dir/clawhub.json")"
default_changelog="$(node - "$skill_dir/CHANGELOG.md" <<'EOF'
const fs = require("node:fs");
const changelogPath = process.argv[2];
const text = fs.readFileSync(changelogPath, "utf8");
const match = text.match(/^##\s+[^\n]+\n\n- ([^\n]+)/m);
if (!match) {
process.exit(1);
}
process.stdout.write(match[1]);
EOF
)"
changelog="-$default_changelog"
clawhub publish "$skill_dir" \
--slug llm-wiki-karpathy \
--name "LLM Wiki Karpathy" \
--version "$version" \
--changelog "$changelog" \
--tags "knowledge-base,research,markdown,wiki,obsidian,multimodal,pdf,image,data,local-first"
Generate practical e-commerce sustainability improvement plans, packaging recommendations, claim-risk cautions, and phased action roadmaps for founders, oper...
---
name: ecommerce-sustainability-advisor
description: Generate practical e-commerce sustainability improvement plans, packaging recommendations, claim-risk cautions, and phased action roadmaps for founders, operations leads, and ESG-minded brand teams. Use when evaluating packaging waste, fulfillment impact, returns, sourcing choices, or marketing claims without live carbon-accounting or compliance APIs.
---
# E-commerce Sustainability Advisor
## Overview
Use this skill to convert rough sustainability concerns into a practical action brief for an e-commerce business. It focuses on operational hotspots such as packaging, shipping, returns, sourcing, and environmental claims.
This MVP is operational and heuristic. It does **not** perform formal carbon accounting, scientific lifecycle assessment, or legal review. It uses built-in best-practice patterns to suggest pragmatic next steps and claim wording cautions.
## Trigger
Use this skill when the user wants to:
- find the biggest sustainability improvement opportunities in an e-commerce operation
- reduce packaging waste or fulfillment impact
- improve sustainability messaging without drifting into greenwashing
- create a short-term and medium-term action roadmap
- align brand, ops, and procurement teams around practical changes
### Example prompts
- "Review our packaging and sustainability claims for a home-goods brand"
- "What are the biggest sustainability wins for our beauty e-commerce business?"
- "We want to sound eco-friendly without making risky claims"
- "Help me reduce returns and packaging waste in our fashion store"
## Workflow
1. Capture the product type, packaging details, fulfillment model, and claim language.
2. Detect the highest-impact operational hotspots.
3. Recommend near-term and medium-term changes that balance impact, feasibility, and customer expectations.
4. Flag risky marketing language or unsupported environmental claims.
5. Return a markdown roadmap with cautions and KPI ideas.
## Inputs
The user can provide any mix of:
- product category or business model
- packaging materials and parcel profile
- sourcing and fulfillment setup
- shipping model and return patterns
- environmental claims or marketing copy
- cost or implementation constraints
## Outputs
Return a markdown brief with:
- executive summary
- impact hotspots
- 90-day action roadmap
- claim wording cautions
- KPI starter pack
- assumptions and limitations
## Safety
- Do not claim scientific or legal certainty.
- Treat claim-risk guidance as a caution, not a legal opinion.
- Avoid unsupported carbon, biodegradability, or recyclability promises.
- State clearly when recommendations rely on incomplete operational data.
## Examples
### Example 1
Input: home-goods brand using oversized boxes and vague green messaging.
Output: identify packaging as the biggest hotspot, recommend right-sizing and recycled-material pilots, and suggest safer wording.
### Example 2
Input: fashion seller with high returns.
Output: highlight returns reduction, fit guidance, and packaging redesign as the main sustainability wins.
## Acceptance Criteria
- Return markdown text.
- Identify at least 3 hotspots, cautions, or action areas.
- Include both operational actions and communication-risk guidance.
- Make it clear that the advice is directional and non-certified.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import List
HOTSPOT_RULES = {
'packaging': ['packaging', 'box', 'plastic', 'bubble wrap', 'oversized', 'single-use', 'mailer'],
'logistics': ['air', 'express', 'shipping', 'split shipment', 'long distance', 'delivery'],
'returns': ['return', 'returns', 'refund', 'exchange', 'fit issue', 'damage'],
'claims': ['eco-friendly', 'green', 'sustainable', 'carbon neutral', 'biodegradable', 'planet-friendly'],
'sourcing': ['supplier', 'material', 'virgin', 'recycled', 'origin', 'procurement'],
}
CLAIM_WORDS = ['eco-friendly', 'green', 'sustainable', 'carbon neutral', 'biodegradable', 'plastic-free']
EVIDENCE_WORDS = ['certified', 'verified', 'fsc', 'recycled content', '%', 'tested', 'audited']
HOTSPOT_EXPLANATIONS = {
'packaging': 'Packaging waste is often the fastest practical win because teams can reduce material, void fill, and carton size without rebuilding the whole business.',
'logistics': 'Shipping mode and split-fulfillment choices can quietly drive impact and cost at the same time.',
'returns': 'Returns create duplicated transport, extra packaging, and inventory handling, so prevention is a strong sustainability lever.',
'claims': 'Claim language can create greenwashing risk if the brand says more than it can support.',
'sourcing': 'Material and supplier choices determine how credible long-term sustainability improvements really are.',
}
class SustainabilityAdvisor:
def __init__(self, text: str):
self.text = (text or '').strip()
self.lower = self.text.lower()
self.hotspots = self._detect_hotspots()
self.claim_risk = self._detect_claim_risk()
def _detect_hotspots(self) -> List[str]:
found = [name for name, keywords in HOTSPOT_RULES.items() if any(word in self.lower for word in keywords)]
if not found:
return ['packaging', 'logistics', 'claims']
ordered = []
for candidate in ['packaging', 'returns', 'logistics', 'claims', 'sourcing']:
if candidate in found:
ordered.append(candidate)
return ordered
def _detect_claim_risk(self) -> bool:
has_claim = any(word in self.lower for word in CLAIM_WORDS)
has_evidence = any(word in self.lower for word in EVIDENCE_WORDS)
return has_claim and not has_evidence
def _roadmap_items(self) -> List[str]:
actions = []
if 'packaging' in self.hotspots:
actions.append('Pilot right-sized packaging, reduce void fill, and document one packaging spec per core SKU family.')
if 'returns' in self.hotspots:
actions.append('Attack avoidable returns through fit guidance, clearer expectations, and damage-prevention packaging.')
if 'logistics' in self.hotspots:
actions.append('Review air-heavy or split-shipment patterns and consolidate where customer promise still remains acceptable.')
if 'claims' in self.hotspots:
actions.append('Rewrite broad sustainability claims into narrower statements tied to specific evidence or current initiatives.')
if 'sourcing' in self.hotspots:
actions.append('Ask suppliers for material composition, recycled-content data, and packaging alternatives before making bigger claims.')
return actions[:4]
def render(self) -> str:
lines: List[str] = []
lines.append('# E-commerce Sustainability Action Brief')
lines.append('')
lines.append('**Method note:** This is a directional operational review. It is not a certified lifecycle assessment, carbon audit, or legal opinion.')
lines.append('')
lines.append('## Executive Summary')
lines.append('- Focus first on the hotspots that are both materially meaningful and operationally feasible for an e-commerce team to change.')
if self.claim_risk:
lines.append('- Current language suggests elevated greenwashing risk because claims appear broader than the evidence provided.')
else:
lines.append('- Messaging risk appears manageable from the provided text, but evidence should still be checked before public claims are expanded.')
lines.append('')
lines.append('## Impact Hotspots')
for hotspot in self.hotspots:
lines.append(f'### {hotspot.title()}')
lines.append(f'- Why it matters: {HOTSPOT_EXPLANATIONS[hotspot]}')
priority = 'High' if hotspot in ('packaging', 'returns', 'claims') else 'Medium'
lines.append(f'- Priority: {priority}')
lines.append('')
lines.append('## 90-Day Action Roadmap')
for idx, action in enumerate(self._roadmap_items(), 1):
lines.append(f'{idx}. {action}')
lines.append('')
lines.append('## Claim Wording Cautions')
if self.claim_risk:
lines.append('- Avoid broad phrases like "eco-friendly" or "carbon neutral" unless you can show current evidence, scope, and boundaries.')
lines.append('- Prefer narrower wording such as "reduced packaging weight" or "contains recycled content" when those facts are documented.')
else:
lines.append('- Keep environmental wording specific, scoped, and evidence-backed.')
lines.append('- Review any new headline claim with supporting proof before launch.')
lines.append('')
lines.append('## KPI Starter Pack')
lines.append('- Packaging weight per order')
lines.append('- Average parcel cube utilization or right-size rate')
lines.append('- Return rate driven by avoidable reasons such as damage or fit confusion')
lines.append('- Share of claims supported by documented evidence')
lines.append('')
lines.append('## Assumptions and Limitations')
lines.append('- The brief assumes the user shared the main operational pain points and not just a marketing concern.')
lines.append('- Formal carbon accounting, legal review, and certification work still require specialist follow-up.')
return '\n'.join(lines)
def handle(user_input: str) -> str:
return SustainabilityAdvisor(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:skill.json
{
"name": "E-commerce Sustainability Advisor",
"slug": "ecommerce-sustainability-advisor",
"version": "1.0.0",
"description": "Advise on e-commerce sustainability improvements including packaging, fulfillment, carbon footprint, and green marketing strategies.",
"author": "Harry (OpenClaw)",
"tags": ["sustainability", "ecommerce", "esg", "green", "packaging"],
"language": "en",
"outputs": "markdown",
"requires_api": false,
"readiness": "stable"
}
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import SustainabilityAdvisor, handle
def test_hotspot_detection_packaging():
advisor = SustainabilityAdvisor('We use oversized boxes with plastic bubble wrap for home goods')
assert 'packaging' in advisor.hotspots
def test_claim_risk_detection():
advisor = SustainabilityAdvisor('Our site says we are eco-friendly and carbon neutral across every order')
assert advisor.claim_risk is True
def test_render_contains_sections():
output = handle('Beauty brand with high returns, express shipping, and vague sustainable messaging')
assert output.startswith('# E-commerce Sustainability Action Brief')
assert '## 90-Day Action Roadmap' in output
assert '## Claim Wording Cautions' in output
def test_kpi_section_present():
output = handle('Review packaging waste and sustainability claims for our ecommerce brand')
assert '## KPI Starter Pack' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Audit mobile shopping flows, identify conversion friction, prioritize UX issues, and suggest experiment backlogs for product teams, CRO specialists, and e-co...
---
name: mobile-commerce-ux-auditor
description: Audit mobile shopping flows, identify conversion friction, prioritize UX issues, and suggest experiment backlogs for product teams, CRO specialists, and e-commerce operators. Use when reviewing mobile storefronts, PDPs, carts, checkouts, or app purchase flows from screenshots or textual flow descriptions without live analytics APIs.
---
# Mobile Commerce UX Auditor
## Overview
Use this skill to convert mobile shopping-flow notes into a commerce-specific UX audit. It focuses on common conversion blockers in mobile homepages, search, PDPs, carts, checkouts, and reorder flows.
This MVP is heuristic. It does **not** analyze real heatmaps, session replays, or analytics APIs. Instead, it applies a structured mobile-commerce checklist and produces a markdown audit that can feed a growth backlog.
## Trigger
Use this skill when the user wants to:
- audit a mobile storefront or app shopping flow
- diagnose low add-to-cart or checkout completion
- identify quick-win conversion fixes
- turn screenshots or flow notes into a prioritized UX backlog
- generate experiment ideas for A/B testing or redesign sprints
### Example prompts
- "Audit our mobile checkout flow"
- "Review these PDP screenshots for conversion friction"
- "Why is mobile add-to-cart low on our skincare store?"
- "Create a quick mobile commerce UX audit from these notes"
## Workflow
1. Capture the business goal and the stages the user is concerned about.
2. Map the input to mobile-commerce stages such as landing, PDP, cart, and checkout.
3. Apply a stage-specific issue library around clarity, trust, hierarchy, form burden, and tap friction.
4. Prioritize findings by likely revenue impact and implementation effort.
5. Return a markdown audit with findings, quick wins, and experiment ideas.
## Inputs
The user can provide any mix of:
- screenshots or textual descriptions of the mobile flow
- funnel goal, such as conversion lift or checkout drop-off reduction
- business context and target market
- known symptoms, such as hidden shipping info or low CTA visibility
- notes about device assumptions or audience
## Outputs
Return a markdown audit with:
- executive summary
- prioritized UX findings by stage
- quick wins
- experiment backlog
- assumptions and evidence gaps
## Safety
- Do not claim access to real analytics or behavioral tools.
- Treat recommendations as heuristics until validated with data.
- Avoid guaranteed uplift claims.
- State clearly when stage coverage is incomplete.
## Examples
### Example 1
Input: mobile PDP and checkout notes for a fashion store.
Output: highlight weak size guidance, delayed shipping clarity, and low-trust checkout signals, then propose specific fixes and tests.
### Example 2
Input: mobile grocery reorder flow.
Output: identify tap friction, reorder-path ambiguity, and cart-message overload, then convert them into a quick-win backlog.
## Acceptance Criteria
- Return markdown text.
- Cover at least 4 mobile-commerce findings or stages.
- Prioritize issues by severity or impact.
- Include at least 3 experiment or quick-win ideas.
FILE:handler.py
#!/usr/bin/env python3
import sys
from typing import Dict, List
STAGE_RULES = {
'Home / Landing': ['home', 'homepage', 'landing', 'hero', 'above the fold'],
'Search / Navigation': ['search', 'navigation', 'menu', 'filter', 'sort'],
'Product Detail Page': ['pdp', 'product page', 'detail page', 'gallery', 'size guide', 'variant'],
'Cart': ['cart', 'basket', 'bag'],
'Checkout': ['checkout', 'payment', 'address', 'form'],
'Post-purchase / Reorder': ['reorder', 'repeat', 'subscription', 'post-purchase', 'post purchase'],
}
ISSUE_LIBRARY = {
'Home / Landing': {
'issue': 'Above-the-fold hierarchy is not focused enough for mobile shoppers.',
'why': 'Too many competing blocks dilute the main value proposition and CTA.',
'fix': 'Reduce competing messages, strengthen the primary CTA, and surface one trust cue early.',
'effort': 'Medium',
},
'Search / Navigation': {
'issue': 'Discovery flow may require too many taps before users reach a viable product set.',
'why': 'Mobile shoppers abandon when filters, sort logic, or categories feel hard to scan.',
'fix': 'Simplify top-level navigation and elevate the highest-value filter shortcuts.',
'effort': 'Medium',
},
'Product Detail Page': {
'issue': 'PDP confidence signals are likely too weak or too buried.',
'why': 'Mobile buyers need fit, shipping, and trust information near the decision point.',
'fix': 'Move proof, shipping summary, and product guidance closer to the CTA and variant area.',
'effort': 'Low-Medium',
},
'Cart': {
'issue': 'Cart may not reinforce urgency, savings logic, or shipping clarity strongly enough.',
'why': 'Users hesitate when the next-step value and cost transparency are unclear.',
'fix': 'Highlight shipping thresholds, bundle logic, and checkout benefits above the fold.',
'effort': 'Low',
},
'Checkout': {
'issue': 'Checkout burden or trust friction is suppressing completion.',
'why': 'Long forms, weak reassurance, and late surprises create major mobile drop-off.',
'fix': 'Shorten the form, enable autofill, and place payment, delivery, and return reassurance near action buttons.',
'effort': 'Medium',
},
'Post-purchase / Reorder': {
'issue': 'The flow likely misses easy repeat-purchase or reorder entry points.',
'why': 'Without low-friction next actions, retention value leaks after the first order.',
'fix': 'Add one-tap reorder cues, reorder reminders, and clearer post-purchase benefit framing.',
'effort': 'Medium',
},
}
GOAL_RULES = {
'checkout': ['checkout', 'payment', 'form'],
'add-to-cart': ['add to cart', 'atc', 'cart'],
'conversion': ['conversion', 'cvr', 'purchase'],
'repeat-purchase': ['repeat', 'reorder', 'retention', 'subscription'],
}
class MobileCommerceAuditor:
def __init__(self, text: str):
self.text = (text or '').strip()
self.lower = self.text.lower()
self.mode = 'deep' if any(word in self.lower for word in ['deep', 'full audit', 'comprehensive', 'full']) else 'quick'
self.goal = self._detect_goal()
self.stages = self._detect_stages()
def _detect_goal(self) -> str:
for goal, keywords in GOAL_RULES.items():
if any(word in self.lower for word in keywords):
return goal
return 'conversion'
def _detect_stages(self) -> List[str]:
stages = [stage for stage, keywords in STAGE_RULES.items() if any(word in self.lower for word in keywords)]
return stages or ['Home / Landing', 'Product Detail Page', 'Cart', 'Checkout']
def _severity(self, stage: str) -> str:
if stage == 'Checkout':
return 'High'
if stage == 'Product Detail Page' and self.goal in ('conversion', 'add-to-cart'):
return 'High'
if stage == 'Post-purchase / Reorder' and self.goal == 'repeat-purchase':
return 'High'
return 'Medium'
def render(self) -> str:
lines: List[str] = []
lines.append('# Mobile Commerce UX Audit')
lines.append('')
lines.append(f'**Mode:** {self.mode}')
lines.append(f'**Primary goal:** {self.goal}')
lines.append('**Method note:** This audit is heuristic and checklist-based. No real analytics, heatmap, or session-replay tool was accessed.')
lines.append('')
lines.append('## Executive Summary')
lines.append('- The audit focuses on conversion-critical mobile moments where clarity, trust, and tap effort matter most.')
lines.append('- Findings are prioritized for revenue relevance first, not purely for visual polish.')
lines.append('')
lines.append('## Prioritized Findings')
for stage in self.stages:
issue = ISSUE_LIBRARY[stage]
lines.append(f'### {stage}')
lines.append(f'- Severity: {self._severity(stage)}')
lines.append(f'- Core issue: {issue["issue"]}')
lines.append(f'- Why it matters: {issue["why"]}')
lines.append(f'- Recommended fix: {issue["fix"]}')
lines.append(f'- Effort: {issue["effort"]}')
lines.append('')
lines.append('## Quick Wins')
quick_wins = [
'Expose one shipping or returns reassurance block closer to the main CTA.',
'Reduce competing mobile modules above the first conversion action.',
'Shorten microcopy where it slows scanning on small screens.',
]
for item in quick_wins:
lines.append(f'- {item}')
lines.append('')
lines.append('## Experiment Backlog')
lines.append('| Test idea | Expected effect | Effort |')
lines.append('|---|---|---|')
lines.append('| Sticky primary CTA with trust cue | Improve click-through to the next step | Low-Medium |')
lines.append('| Earlier shipping / returns summary | Reduce hesitation near purchase action | Low |')
lines.append('| Shorter checkout form or autofill emphasis | Lift completion on mobile checkout | Medium |')
lines.append('| Variant guidance closer to CTA | Improve PDP confidence and add-to-cart rate | Medium |')
lines.append('')
lines.append('## Assumptions and Gaps')
lines.append('- This output assumes the provided notes represent the main mobile path and not an edge-case flow.')
lines.append('- Real analytics and usability evidence should be used to validate the order of fixes.')
return '\n'.join(lines)
def handle(user_input: str) -> str:
return MobileCommerceAuditor(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:skill.json
{
"name": "Mobile Commerce UX Auditor",
"slug": "mobile-commerce-ux-auditor",
"version": "1.0.0",
"description": "Audit mobile commerce user experience, evaluate page speed, checkout flow, navigation, and provide UX improvement recommendations.",
"author": "Harry (OpenClaw)",
"tags": ["mobile", "ux", "ecommerce", "audit", "conversion-rate"],
"language": "en",
"outputs": "markdown",
"requires_api": false,
"readiness": "stable"
}
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import MobileCommerceAuditor, handle
def test_mode_detection_deep():
auditor = MobileCommerceAuditor('Please run a full audit of our mobile checkout')
assert auditor.mode == 'deep'
def test_goal_detection_checkout():
auditor = MobileCommerceAuditor('Our checkout conversion is dropping on mobile')
assert auditor.goal == 'checkout'
def test_stage_detection():
auditor = MobileCommerceAuditor('Review the homepage, PDP, cart, and checkout on mobile')
assert 'Checkout' in auditor.stages
assert 'Cart' in auditor.stages
def test_render_contains_sections():
output = handle('Audit our mobile PDP and checkout for a fashion store')
assert output.startswith('# Mobile Commerce UX Audit')
assert '## Prioritized Findings' in output
assert '## Experiment Backlog' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Curate recurring subscription box concepts, item mixes, pricing tiers, add-on ideas, and replenishment warnings for DTC brands, merchandising teams, and subs...
---
name: subscription-box-curator
description: Curate recurring subscription box concepts, item mixes, pricing tiers, add-on ideas, and replenishment warnings for DTC brands, merchandising teams, and subscription operators. Use when planning monthly or quarterly boxes, balancing persona fit, theme novelty, margin discipline, inventory constraints, and retention goals without live catalog APIs.
---
# Subscription Box Curator
## Overview
Use this skill to turn rough subscription-box inputs into a practical curation brief. It is designed for beauty boxes, snack boxes, pet boxes, hobby kits, wellness subscriptions, and book clubs.
This MVP is descriptive and template-based. It does **not** connect to Shopify, inventory systems, forecasting tools, or supplier APIs. It uses built-in heuristics to produce a markdown recommendation set that a merchandising or growth team can refine.
## Trigger
Use this skill when the user wants to:
- plan a monthly or quarterly subscription box
- reduce subscription churn through stronger curation logic
- generate seasonal themes and item architectures
- balance novelty, margin, replenishment, and fulfillment feasibility
- create add-on ideas, pricing tiers, or launch notes for a recurring box
### Example prompts
- "Help me plan a May self-care subscription box under $28 landed cost"
- "Our pet box retention is weak, suggest new monthly themes"
- "Create three snack-box concepts for students and young professionals"
- "We need a quarterly book box plan with upsells"
## Workflow
1. Collect the category, audience, budget, seasonality, and business goal.
2. Detect the likely box category and the strongest operating constraints.
3. Generate 3 theme directions with item architecture, rationale, and pricing guidance.
4. Add add-on ideas, replenishment watchouts, and practical next moves.
5. Present the result as a markdown planning brief.
## Inputs
The user can provide any mix of:
- subscription category or product type
- target audience or customer segments
- landed-cost target or budget range
- season, month, or campaign context
- inventory and shipping constraints
- churn, retention, launch, or margin goals
- feedback from prior boxes
## Outputs
Return a markdown brief with:
- executive summary
- input snapshot
- 3 recommended box concepts
- indicative cost and pricing guidance
- add-on and upsell ideas
- replenishment warnings and assumptions
- next-step actions for merchandising and launch planning
## Safety
- Do not claim access to real inventory, demand, supplier, or margin data.
- Keep recommendations advisory and assumption-based.
- Avoid hard promises about retention uplift, sell-through, or forecast accuracy.
- Flag missing data when the user does not provide category, budget, or customer details.
## Examples
### Example 1
Input: beauty subscription, May launch, $28 landed-cost target, young professionals, retention focus.
Output: three self-care theme directions, recommended hero/staple/surprise mix, suggested sell-price bands, and replenishment cautions for consumable SKUs.
### Example 2
Input: pet subscription with churn concerns.
Output: rotating play + treat + care concepts, persona-fit notes, and add-on ideas that increase perceived freshness without overcomplicating fulfillment.
## Acceptance Criteria
- Return markdown text, not a vague chat-only answer.
- Produce at least 3 concept options.
- Include both merchandising rationale and operational caveats.
- Clearly state that the result is heuristic and non-API-based.
FILE:handler.py
#!/usr/bin/env python3
import re
import sys
from typing import Dict, List, Optional
CATEGORY_KEYWORDS = {
'beauty': ['beauty', 'skincare', 'makeup', 'cosmetic', 'self-care', 'self care'],
'snack': ['snack', 'food', 'tea', 'coffee', 'treat', 'drink'],
'pet': ['pet', 'dog', 'cat', 'puppy', 'kitten'],
'book': ['book', 'reading', 'novel', 'fiction', 'nonfiction'],
'wellness': ['wellness', 'fitness', 'mindfulness', 'supplement', 'recovery'],
'hobby': ['hobby', 'craft', 'kit', 'diy', 'creative'],
}
DEFAULT_BUDGET = {
'beauty': 28,
'snack': 22,
'pet': 26,
'book': 24,
'wellness': 30,
'hobby': 32,
}
THEME_LIBRARY = {
'beauty': [
{
'name': 'Reset Ritual',
'angle': 'Blend one hero treatment with easy-repeat staples for a calm self-care story.',
'structure': ['hero treatment', 'routine staple', 'surprise comfort extra'],
'replenishment': 'Watch any fast-moving serum, cleanser, or mask with less than 6 weeks of cover.',
},
{
'name': 'Desk-to-Dinner Glow',
'angle': 'Position the box around quick routine upgrades for busy weekdays.',
'structure': ['prep item', 'on-the-go enhancer', 'sensory delight'],
'replenishment': 'Keep compact SKUs and leak-prone items in buffer stock for packing reliability.',
},
{
'name': 'Seasonal Calm Kit',
'angle': 'Use a seasonal ritual narrative to create freshness without changing every core SKU.',
'structure': ['seasonal hero', 'habit anchor', 'theme accessory'],
'replenishment': 'Reserve flexible accessory substitutions in case seasonal packaging slips.',
},
],
'snack': [
{
'name': 'Better Break Box',
'angle': 'Mix familiar comfort with one discovery flavor to keep repeat boxes exciting.',
'structure': ['anchor snack', 'functional treat', 'surprise flavor'],
'replenishment': 'Track shelf-life windows and avoid overcommitting to novelty SKUs with low repeatability.',
},
{
'name': 'Workday Energy Drop',
'angle': 'Support midday productivity with portion-controlled, portable items.',
'structure': ['focus snack', 'hydration companion', 'reward bite'],
'replenishment': 'Secure replacements for fragile or melt-sensitive items before peak heat periods.',
},
{
'name': 'Seasonal Discovery Flight',
'angle': 'Rotate regional or limited flavors while preserving one recognizable favorite.',
'structure': ['seasonal headline item', 'safe favorite', 'conversation starter'],
'replenishment': 'Build alternates for imported or seasonal SKUs that may sell out early.',
},
],
'pet': [
{
'name': 'Play + Treat + Care',
'angle': 'Balance fun, reward, and wellness so the box feels complete instead of random.',
'structure': ['play item', 'treat item', 'care item'],
'replenishment': 'Monitor treat inventory and size-variant coverage to avoid last-minute substitutions.',
},
{
'name': 'Enrichment Refresh',
'angle': 'Use rotating enrichment themes to fight subscription fatigue.',
'structure': ['challenge toy', 'reward snack', 'owner convenience extra'],
'replenishment': 'Keep a substitution list for oversized toys or region-restricted treat ingredients.',
},
{
'name': 'Seasonal Bonding Box',
'angle': 'Center the box on owner-pet rituals that create emotional retention value.',
'structure': ['shared activity item', 'comfort item', 'photo-worthy bonus'],
'replenishment': 'Protect packaging timelines for photo-led seasonal extras and soft goods.',
},
],
'book': [
{
'name': 'Anchor Read + Companion Gift',
'angle': 'Pair one clear reading promise with a small companion item that amplifies the theme.',
'structure': ['headline title', 'theme companion', 'community prompt'],
'replenishment': 'Confirm title availability and keep one approved alternate title in the same mood lane.',
},
{
'name': 'Genre Journey Box',
'angle': 'Rotate genre-led curation while preserving a familiar community experience.',
'structure': ['genre lead title', 'immersion accessory', 'discussion insert'],
'replenishment': 'Protect against publisher delays and keep printable inserts flexible.',
},
{
'name': 'Quarterly Reflection Edition',
'angle': 'Use slower seasonal rhythms to justify a premium-feeling box with reflection tools.',
'structure': ['main read', 'annotation tool', 'ritual extra'],
'replenishment': 'Leave room to swap stationery or gift extras if print or import lead times drift.',
},
],
'wellness': [
{
'name': 'Reset and Recover',
'angle': 'Lead with low-friction habits that feel useful from day one.',
'structure': ['habit anchor', 'recovery helper', 'motivation nudge'],
'replenishment': 'Backstop any consumable or compliance-sensitive item with a non-consumable alternative.',
},
{
'name': 'Morning Momentum',
'angle': 'Frame the box around routine consistency and easy wins.',
'structure': ['daily starter', 'supporting tool', 'surprise delight'],
'replenishment': 'Keep a fallback for glass, liquid, or temperature-sensitive items.',
},
{
'name': 'Seasonal Reset Circuit',
'angle': 'Refresh the subscription through seasonal behavior cues rather than SKU chaos.',
'structure': ['seasonal focus item', 'core staple', 'reflection prompt'],
'replenishment': 'Limit bespoke inserts that are hard to reprint late in the cycle.',
},
],
'hobby': [
{
'name': 'Starter Win Kit',
'angle': 'Make the box feel achievable, rewarding, and easy to begin immediately.',
'structure': ['headline project item', 'support tool', 'bonus extra'],
'replenishment': 'Protect specialty components and keep one simpler substitute for scarce parts.',
},
{
'name': 'Skill-Building Series',
'angle': 'Sequence the box so each month advances one visible capability.',
'structure': ['core project piece', 'practice aid', 'showcase add-on'],
'replenishment': 'Avoid single-source materials without backup options.',
},
{
'name': 'Seasonal Inspiration Drop',
'angle': 'Use calendar themes to create freshness while preserving a repeatable assembly process.',
'structure': ['seasonal craft lead', 'color or texture set', 'shareable bonus'],
'replenishment': 'Reserve swappable colorways or motifs if a seasonal design asset slips.',
},
],
}
ADD_ONS = {
'beauty': ['mini refill add-on', 'premium tool upgrade', 'giftable duo pack'],
'snack': ['office share pack', 'limited-edition flavor booster', 'sampler refill'],
'pet': ['extra treat pouch', 'premium chew upgrade', 'birthday add-on'],
'book': ['annotator bundle', 'premium bookmark set', 'author note insert'],
'wellness': ['habit tracker card', 'premium recovery extra', 'refill pouch'],
'hobby': ['advanced material pack', 'community challenge insert', 'premium finishing tool'],
}
PERSONA_RULES = {
'students': ['student', 'campus', 'college'],
'busy professionals': ['busy', 'professional', 'office', 'workday'],
'premium enthusiasts': ['premium', 'luxury', 'high-end', 'enthusiast'],
'families': ['family', 'kids', 'parent'],
'gift buyers': ['gift', 'present', 'holiday'],
}
SEASON_RULES = {
'spring': ['spring', 'march', 'april', 'may'],
'summer': ['summer', 'june', 'july', 'august'],
'autumn': ['autumn', 'fall', 'september', 'october', 'november'],
'winter': ['winter', 'december', 'january', 'february'],
}
GOAL_RULES = {
'retention': ['retention', 'churn', 'keep subscribers', 'renewal'],
'margin': ['margin', 'profit', 'gross margin'],
'launch': ['launch', 'new box', 'new subscription', 'acquisition'],
}
class SubscriptionBoxPlanner:
def __init__(self, text: str):
self.text = (text or '').strip()
self.lower = self.text.lower()
self.category = self._detect_category()
self.budget = self._extract_budget()
self.goal = self._detect_goal()
self.season = self._detect_season()
self.personas = self._detect_personas()
self.constraints = self._detect_constraints()
def _detect_category(self) -> str:
scored = []
for category, keywords in CATEGORY_KEYWORDS.items():
hits = sum(1 for word in keywords if word in self.lower)
scored.append((hits, category))
best_hits, best_category = max(scored)
return best_category if best_hits > 0 else 'wellness'
def _extract_budget(self) -> int:
matches = re.findall(r'\$\s?(\d{1,3})|under\s?\$?(\d{1,3})|(\d{1,3})\s?(?:usd|dollars)', self.lower)
for match in matches:
for part in match:
if part:
return int(part)
return DEFAULT_BUDGET[self.category]
def _detect_goal(self) -> str:
for goal, keywords in GOAL_RULES.items():
if any(word in self.lower for word in keywords):
return goal
return 'balanced'
def _detect_season(self) -> str:
for season, keywords in SEASON_RULES.items():
if any(word in self.lower for word in keywords):
return season
return 'always-on'
def _detect_personas(self) -> List[str]:
personas = [name for name, keywords in PERSONA_RULES.items() if any(word in self.lower for word in keywords)]
return personas or ['core subscribers', 'value seekers', 'loyal fans']
def _detect_constraints(self) -> List[str]:
flags = []
if any(word in self.lower for word in ['inventory', 'stock', 'restock', 'supply']):
flags.append('Inventory depth matters for at least one key SKU.')
if any(word in self.lower for word in ['ship', 'shipping', 'fragile', 'cold', 'liquid']):
flags.append('Fulfillment simplicity and parcel reliability should influence the final mix.')
if any(word in self.lower for word in ['budget', 'margin', 'cost', 'under $', 'under$']):
flags.append('Cost discipline is an explicit decision filter.')
if any(word in self.lower for word in ['feedback', 'review', 'complaint']):
flags.append('Past customer feedback should shape what stays familiar versus what rotates.')
return flags or ['Inputs were limited, so the plan uses a general subscription-box baseline.']
def _price_band(self, landed_cost: int) -> str:
low = round(landed_cost * 2.1)
high = round(landed_cost * 2.6)
return f'low-high'
def _cost_for_concept(self, index: int) -> int:
base = self.budget
if self.goal == 'margin':
return max(12, base - 3 + index)
if self.goal == 'retention':
return base + index
return base - 1 + index
def _theme_notes(self, concept: Dict[str, str], index: int) -> str:
persona = self.personas[index % len(self.personas)]
if self.goal == 'retention':
emphasis = 'Keep one dependable repeatable item and one rotating surprise to reduce box fatigue.'
elif self.goal == 'margin':
emphasis = 'Let the hero item carry the story while support items stay operationally simple.'
elif self.goal == 'launch':
emphasis = 'Use a clearer headline theme so first-time subscribers instantly understand the promise.'
else:
emphasis = 'Balance discovery, practicality, and merchandising freshness across the mix.'
return f'Best fit for **{persona}**. {emphasis}'
def render(self) -> str:
concepts = THEME_LIBRARY[self.category][:3]
lines: List[str] = []
lines.append('# Subscription Box Curation Plan')
lines.append('')
lines.append(f'**Category:** {self.category.title()}')
lines.append(f'**Primary objective:** {self.goal}')
lines.append(f'**Seasonal context:** {self.season}')
lines.append(f'**Budget anchor:** self.budget landed-cost target')
lines.append('**Method note:** This plan is heuristic and template-based. No live catalog, inventory, or pricing API was used.')
lines.append('')
lines.append('## Input Snapshot')
lines.append(f'- Suggested audience lanes: {", ".join(self.personas[:3])}')
for flag in self.constraints:
lines.append(f'- Constraint signal: {flag}')
lines.append('')
lines.append('## Recommended Box Concepts')
for idx, concept in enumerate(concepts, 1):
landed_cost = self._cost_for_concept(idx)
lines.append(f'### Concept {idx}: {concept["name"]}')
lines.append(f'- Theme angle: {concept["angle"]}')
lines.append(f'- Suggested item architecture: {", ".join(concept["structure"])}')
lines.append(f'- Indicative landed cost: ~landed_cost')
lines.append(f'- Indicative sell-price band: {self._price_band(landed_cost)}')
lines.append(f'- Why it works: {self._theme_notes(concept, idx - 1)}')
lines.append(f'- Add-on idea: {ADD_ONS[self.category][(idx - 1) % len(ADD_ONS[self.category])]}')
lines.append(f'- Replenishment watchout: {concept["replenishment"]}')
lines.append('')
lines.append('## Merchandising Matrix')
lines.append('| Concept | Novelty level | Margin discipline | Fulfillment ease | Retention value |')
lines.append('|---|---|---|---|---|')
for idx, concept in enumerate(concepts, 1):
novelty = 'High' if idx == 2 else 'Medium'
margin = 'High' if self.goal == 'margin' and idx == 1 else 'Medium'
fulfillment = 'High' if idx != 2 else 'Medium'
retention = 'High' if self.goal == 'retention' or idx == 1 else 'Medium'
lines.append(f'| {concept["name"]} | {novelty} | {margin} | {fulfillment} | {retention} |')
lines.append('')
lines.append('## Risks and Assumptions')
lines.append('- Theme quality improves if the user later provides SKU margin, inventory cover, and subscriber feedback by segment.')
lines.append('- Pricing guidance is directional and should be reconciled with shipping, packaging, and discount rules.')
lines.append('- Final assortment choices should be checked against procurement timing and substitution tolerance.')
lines.append('')
lines.append('## Next Moves')
lines.append('1. Pick one concept as the control box and one as the challenger concept.')
lines.append('2. Map real SKUs into the hero / staple / surprise architecture.')
lines.append('3. Validate landed cost, pack-out complexity, and replenishment coverage before launch lock.')
return '\n'.join(lines)
def handle(user_input: str) -> str:
return SubscriptionBoxPlanner(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import SubscriptionBoxPlanner, handle
def test_category_detection_pet():
planner = SubscriptionBoxPlanner('We run a pet subscription with dog toys and treats')
assert planner.category == 'pet'
def test_budget_extraction():
planner = SubscriptionBoxPlanner('Need a May beauty box under $28 landed cost')
assert planner.budget == 28
def test_goal_detection_retention():
planner = SubscriptionBoxPlanner('Our subscription churn is rising, help with retention')
assert planner.goal == 'retention'
def test_render_contains_three_concepts():
output = handle('Plan a beauty subscription box for busy professionals under $28')
assert output.startswith('# Subscription Box Curation Plan')
assert output.count('### Concept ') >= 3
assert '## Risks and Assumptions' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Compare cross-border e-commerce shipping options, packaging tactics, customs-risk signals, and speed-versus-cost tradeoffs for sellers, operations teams, and...
---
name: cross-border-logistics-optimizer
description: Compare cross-border e-commerce shipping options, packaging tactics, customs-risk signals, and speed-versus-cost tradeoffs for sellers, operations teams, and marketplace operators. Use when choosing routes, carriers, declaration practices, or packaging plans for parcel shipments without relying on live carrier APIs.
---
# Cross-border Logistics Optimizer
## Overview
Use this skill to turn shipment notes into a practical route recommendation brief. It is optimized for parcel-level e-commerce decisions, especially when a seller needs to balance shipping cost, transit speed, customs reliability, and packaging risk.
This MVP is advisory only. It does **not** fetch live rates, customs rules, or carrier SLAs. It relies on built-in logistics heuristics and common e-commerce exception patterns.
## Trigger
Use this skill when the user wants to:
- compare shipping lanes for international e-commerce orders
- choose between cheap, fast, or low-risk logistics modes
- identify customs-delay risks and declaration pitfalls
- reduce dimensional-weight or packaging mistakes
- create a short decision memo for an ops team
### Example prompts
- "Compare shipping options from China to Germany for cosmetics"
- "We need the safest route to Brazil for a fragile parcel"
- "How should I package low-value accessories going to Canada?"
- "Help me choose between cheap and fast cross-border shipping"
## Workflow
1. Capture the origin, destination, parcel profile, product type, and business priority.
2. Detect likely risk signals such as customs sensitivity, dimensional-weight pressure, or restricted goods handling.
3. Compare three lane patterns with tradeoff notes.
4. Recommend the best-fit route, packaging posture, and documentation focus.
5. Return the result as a markdown decision brief.
## Inputs
The user can provide any mix of:
- origin and destination countries
- weight and parcel dimensions
- product category or sensitivity
- declared value
- carrier preferences or service-level goals
- priority, such as cheapest, fastest, or safest
- known issue patterns, such as customs delay or damage
## Outputs
Return a markdown report with:
- executive summary
- route comparison table
- recommended shipping plan
- packaging checklist
- customs and exception risks
- assumptions and follow-up actions
## Safety
- Do not claim access to live rates or carrier systems.
- Treat all customs and compliance guidance as directional, not legal advice.
- Avoid guaranteed delivery claims.
- Call out when the input lacks dimensions, value, or product sensitivity details.
## Examples
### Example 1
Input: China to Germany, cosmetics, tracked service preferred.
Output: recommend a duty-aware direct line over the cheapest untracked option, plus declaration and leakage-control notes.
### Example 2
Input: US to Canada, small accessories, cost priority.
Output: compare economy tracked, direct line, and express courier options, with dimensional-weight mitigation advice.
## Acceptance Criteria
- Return markdown text.
- Include a 3-option lane comparison.
- Explain why the recommended lane fits the stated priority.
- Include at least one packaging note and one customs-risk note.
FILE:handler.py
#!/usr/bin/env python3
import re
import sys
from typing import Dict, List, Optional, Tuple
COUNTRY_ALIASES = {
'china': 'China',
'germany': 'Germany',
'brazil': 'Brazil',
'canada': 'Canada',
'united states': 'United States',
'usa': 'United States',
'us': 'United States',
'uk': 'United Kingdom',
'united kingdom': 'United Kingdom',
'france': 'France',
'australia': 'Australia',
'japan': 'Japan',
}
PRODUCT_RULES = {
'cosmetics': ['cosmetic', 'beauty', 'skincare', 'cream', 'liquid', 'serum'],
'battery electronics': ['battery', 'electronics', 'device', 'bluetooth', 'charger', 'power bank'],
'apparel': ['apparel', 'fashion', 'clothing', 'shirt', 'dress'],
'supplements': ['supplement', 'powder', 'capsule', 'vitamin'],
'accessories': ['accessory', 'jewelry', 'case', 'cable'],
}
PRIORITY_RULES = {
'fastest': ['fastest', 'urgent', 'express', 'quickest'],
'cheapest': ['cheapest', 'low cost', 'lowest cost', 'budget'],
'safest': ['safest', 'reliable', 'low risk', 'secure'],
}
LANES = [
{
'name': 'Economy Tracked Packet',
'cost': 'Low',
'speed': 'Slow',
'customs': 'Medium-High',
'tracking': 'Basic',
'best_for': 'cost-sensitive, low-risk parcels',
},
{
'name': 'Duty-aware Direct Line',
'cost': 'Medium',
'speed': 'Medium',
'customs': 'Low-Medium',
'tracking': 'Good',
'best_for': 'balanced e-commerce lanes with customs control',
},
{
'name': 'Express Courier',
'cost': 'High',
'speed': 'Fast',
'customs': 'Medium',
'tracking': 'Excellent',
'best_for': 'urgent deliveries or premium customer promises',
},
]
class LogisticsOptimizer:
def __init__(self, text: str):
self.text = (text or '').strip()
self.lower = self.text.lower()
self.origin, self.destination = self._extract_countries()
self.priority = self._detect_priority()
self.product_type = self._detect_product_type()
self.weight_kg = self._extract_weight_kg()
self.dimensions_cm = self._extract_dimensions_cm()
self.declared_value = self._extract_declared_value()
self.risk_flags = self._build_risk_flags()
def _extract_countries(self) -> Tuple[str, str]:
origin = 'Origin not specified'
destination = 'Destination not specified'
route_match = re.search(r'from\s+([a-z\s]+?)\s+to\s+([a-z\s]+?)(?:\s+for|\s+with|\s+at|\s+via|$)', self.lower)
if route_match:
raw_origin = route_match.group(1).strip()
raw_destination = route_match.group(2).strip()
origin = COUNTRY_ALIASES.get(raw_origin, raw_origin.title())
destination = COUNTRY_ALIASES.get(raw_destination, raw_destination.title())
return origin, destination
found = []
for key, display in COUNTRY_ALIASES.items():
if key in self.lower and display not in found:
found.append(display)
if found:
origin = found[0]
if len(found) > 1:
destination = found[1]
return origin, destination
def _detect_priority(self) -> str:
for priority, keywords in PRIORITY_RULES.items():
if any(word in self.lower for word in keywords):
return priority
return 'balanced'
def _detect_product_type(self) -> str:
for name, keywords in PRODUCT_RULES.items():
if any(word in self.lower for word in keywords):
return name
return 'general merchandise'
def _extract_weight_kg(self) -> Optional[float]:
match = re.search(r'(\d+(?:\.\d+)?)\s*(kg|g|lb|lbs)', self.lower)
if not match:
return None
value = float(match.group(1))
unit = match.group(2)
if unit == 'g':
return round(value / 1000, 2)
if unit in ('lb', 'lbs'):
return round(value * 0.4536, 2)
return value
def _extract_dimensions_cm(self) -> Optional[Tuple[int, int, int]]:
match = re.search(r'(\d{1,3})\s*[x×]\s*(\d{1,3})\s*[x×]\s*(\d{1,3})\s*(cm|in)', self.lower)
if not match:
return None
dims = [int(match.group(1)), int(match.group(2)), int(match.group(3))]
unit = match.group(4)
if unit == 'in':
dims = [round(d * 2.54) for d in dims]
return tuple(dims)
def _extract_declared_value(self) -> Optional[int]:
match = re.search(r'\$\s?(\d{1,5})|(\d{1,5})\s?(?:usd|dollars)', self.lower)
if not match:
return None
for part in match.groups():
if part:
return int(part)
return None
def _build_risk_flags(self) -> List[str]:
flags: List[str] = []
if self.destination == 'Brazil':
flags.append('Brazil lanes often face documentation scrutiny and longer customs dwell time.')
if self.destination == 'Germany':
flags.append('Germany-bound parcels need precise product description and tax-ready paperwork.')
if self.destination == 'Canada':
flags.append('Canada lanes can punish dimensional weight on light but bulky parcels.')
if self.product_type == 'cosmetics':
flags.append('Cosmetics need cleaner ingredient/category descriptions and leak-resistant packaging.')
if self.product_type == 'battery electronics':
flags.append('Battery goods may trigger carrier restrictions and special handling checks.')
if self.weight_kg and self.weight_kg > 2:
flags.append('Heavier parcels may lose economy-lane savings quickly.')
if self.dimensions_cm and sum(self.dimensions_cm) > 90:
flags.append('Parcel size may attract dimensional-weight surcharges or oversize screening.')
if any(word in self.lower for word in ['customs', 'delay', 'sensitive', 'duty']):
flags.append('The user already signaled customs sensitivity, so reliability matters more than headline price.')
return flags or ['Inputs are limited, so the analysis uses a generic parcel-shipping baseline.']
def _recommended_lane(self) -> Dict[str, str]:
if self.priority == 'fastest':
return LANES[2]
if self.priority == 'cheapest' and self.product_type in ('general merchandise', 'accessories', 'apparel') and len(self.risk_flags) <= 2:
return LANES[0]
if self.product_type in ('cosmetics', 'battery electronics', 'supplements') or any('customs' in flag.lower() for flag in self.risk_flags):
return LANES[1]
if self.priority == 'safest':
return LANES[1]
return LANES[1]
def _packaging_notes(self) -> List[str]:
notes = []
if self.dimensions_cm and sum(self.dimensions_cm) > 90:
notes.append('Reduce void fill and carton size to control dimensional-weight billing.')
if self.product_type == 'cosmetics':
notes.append('Use leak-control sealing and isolate liquid SKUs from outer-wall impact points.')
if self.product_type == 'battery electronics':
notes.append('Use inner cushioning plus clear battery labeling before handoff to the carrier.')
if not notes:
notes.append('Use a right-sized carton and document pack-out consistency to reduce exceptions.')
notes.append('Place a clean commercial description and value summary where the fulfillment team cannot miss it.')
return notes
def render(self) -> str:
recommended = self._recommended_lane()
lines: List[str] = []
lines.append('# Cross-border Logistics Decision Brief')
lines.append('')
lines.append(f'**Route:** {self.origin} to {self.destination}')
lines.append(f'**Business priority:** {self.priority}')
lines.append(f'**Product type:** {self.product_type}')
lines.append(f'**Weight estimate:** {self.weight_kg if self.weight_kg is not None else "Not provided"}')
lines.append(f'**Declared value:** self.declared_value' if self.declared_value is not None else '**Declared value:** Not provided')
lines.append('**Method note:** This is a heuristic lane comparison. No live carrier rate or customs API was used.')
lines.append('')
lines.append('## Route Comparison')
lines.append('| Option | Cost | Speed | Customs risk | Tracking | Best fit |')
lines.append('|---|---|---|---|---|---|')
for lane in LANES:
lines.append(f'| {lane["name"]} | {lane["cost"]} | {lane["speed"]} | {lane["customs"]} | {lane["tracking"]} | {lane["best_for"]} |')
lines.append('')
lines.append('## Recommended Plan')
lines.append(f'- Recommended lane: **{recommended["name"]}**')
if recommended['name'] == 'Express Courier':
rationale = 'Choose this when speed is the clear priority and the customer promise justifies the higher spend.'
elif recommended['name'] == 'Economy Tracked Packet':
rationale = 'Choose this when cost matters most and the parcel profile is low-risk enough to tolerate slower movement.'
else:
rationale = 'Choose this when the business needs a balanced lane with better customs control than the cheapest option.'
lines.append(f'- Why this fits: {rationale}')
lines.append(f'- Operational reminder: Match the lane to the real parcel profile before handoff, especially if weight, value, or product sensitivity changes.')
lines.append('')
lines.append('## Packaging Checklist')
for note in self._packaging_notes():
lines.append(f'- {note}')
lines.append('')
lines.append('## Customs and Exception Risks')
for flag in self.risk_flags:
lines.append(f'- {flag}')
lines.append('')
lines.append('## Assumptions and Next Steps')
lines.append('- Confirm the exact carrier service, not just the brand, because service families behave differently.')
lines.append('- Recheck declaration wording, parcel dimensions, and value before the final label is purchased.')
lines.append('- If the lane is mission critical, compare one live quote after this heuristic shortlisting step.')
return '\n'.join(lines)
def handle(user_input: str) -> str:
return LogisticsOptimizer(user_input).render()
if __name__ == '__main__':
payload = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()
print(handle(payload))
FILE:tests/test_handler.py
#!/usr/bin/env python3
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import LogisticsOptimizer, handle
def test_priority_detection_fastest():
tool = LogisticsOptimizer('Need the fastest route from China to Germany for electronics')
assert tool.priority == 'fastest'
def test_country_extraction():
tool = LogisticsOptimizer('Compare lanes from China to Germany for cosmetics')
assert tool.origin == 'China'
assert tool.destination == 'Germany'
def test_risk_flags_for_brazil_cosmetics():
tool = LogisticsOptimizer('Safest route from China to Brazil for cosmetics with customs delay concerns')
joined = ' '.join(tool.risk_flags).lower()
assert 'brazil' in joined
assert 'cosmetics' in joined or 'ingredient' in joined or 'leak' in joined
def test_render_contains_sections():
output = handle('Cheapest way to ship accessories from US to Canada at 0.5kg and 30x20x10cm')
assert output.startswith('# Cross-border Logistics Decision Brief')
assert '## Route Comparison' in output
assert '## Packaging Checklist' in output
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Generates influencer shortlist tiers, analyzes creator fit by campaign goal, recommends partnership mode, and drafts outreach briefs for e-commerce brands.
# Influencer Partnership Scout
## Overview
Influencer Partnership Scout helps SMB e-commerce brands shortlist likely creator fits, choose partnership modes, and draft outreach logic. This MVP is descriptive only, with no real creator API access.
## Trigger
Use this skill when the user wants to:
- find likely creator archetypes for a campaign
- compare influencer fit for awareness, UGC, affiliate, or conversion goals
- decide between gifting, affiliate, paid post, or ambassador strategy
- generate an outreach shortlist and contact brief
### Example prompts
- "Help me shortlist creators for my skincare brand"
- "Should we use micro influencers or larger creators?"
- "Give me outreach strategy for a pet accessory launch"
- "Rank influencer fit for UGC + conversion"
## Workflow
1. Capture brand brief and campaign goal.
2. Infer creator fit, audience fit, and content compatibility.
3. Recommend partnership mode and shortlist tiers.
4. Generate outreach and risk notes in markdown.
## Inputs
- brand name or category
- market / region
- product price range
- campaign goal: awareness / UGC / affiliate / conversion
- preferred platform and follower range
- optional candidate handles or niche keywords
## Outputs
- influencer shortlist framework
- partnership fit analysis
- recommended partnership mode
- contact brief and outreach angle
## Safety
- No live social platform data
- No guarantee of creator pricing, reply rate, or conversion
- Final brand-safety review should be done by a human
## Acceptance Criteria
- Must return markdown
- Must include shortlist tiers
- Must include fit analysis and risk notes
- Must include outreach recommendation and contact brief
FILE:ACCEPTANCE.md
# Acceptance Criteria
- Returns markdown brief
- Includes shortlist tiers
- Includes partnership fit analysis
- Includes recommended partnership mode
- Includes outreach recommendation and risk notes
- Explicitly states heuristic / no real API access
FILE:handler.py
#!/usr/bin/env python3
import sys
PLATFORM_RULES = {
'tiktok': ('short-form demo', 'nano / micro creators'),
'instagram': ('visual lifestyle storytelling', 'micro / mid-tier creators'),
'youtube': ('deeper product education', 'mid-tier creators'),
'xiaohongshu': ('trust-led recommendation style', 'nano / micro creators'),
}
GOAL_MODES = {
'awareness': 'Paid post or gifting with repost rights',
'ugc': 'Gifting + usage-based creator brief',
'affiliate': 'Affiliate-first with tracked code',
'conversion': 'Micro creator seeding plus affiliate or paid hybrid',
}
RISK_NOTES = [
'High follower count does not guarantee audience-brand fit.',
'Overly commercial feeds may underperform for trust-sensitive products.',
'Heuristic creator fit should be validated with manual content review before outreach.',
]
class ScoutEngine:
def __init__(self, text: str):
self.text = text.strip()
self.lower = self.text.lower()
self.platform = self._detect_platform()
self.goal = self._detect_goal()
self.tier = self._detect_tier()
self.category = self._detect_category()
def _detect_platform(self):
for p in PLATFORM_RULES:
if p in self.lower:
return p
if '小红书' in self.text:
return 'xiaohongshu'
return 'tiktok'
def _detect_goal(self):
for g in GOAL_MODES:
if g in self.lower:
return g
if 'ugc' in self.lower:
return 'ugc'
if 'awareness' in self.lower:
return 'awareness'
if 'affiliate' in self.lower:
return 'affiliate'
if '转化' in self.text:
return 'conversion'
return 'conversion'
def _detect_tier(self):
if 'nano' in self.lower or '1k' in self.lower:
return 'nano'
if 'micro' in self.lower or '10k' in self.lower or '80k' in self.lower:
return 'micro'
if 'mid' in self.lower or '100k' in self.lower:
return 'mid-tier'
return 'micro'
def _detect_category(self):
for c in ['skincare', 'pet', 'home', 'beauty', 'fashion', 'supplement', 'organizer']:
if c in self.lower:
return c
if '护肤' in self.text:
return 'skincare'
if '宠物' in self.text:
return 'pet'
if '收纳' in self.text:
return 'home organization'
return 'ecommerce brand'
def fit_analysis(self):
content_style, default_tier = PLATFORM_RULES[self.platform]
audience_fit = 'High' if self.category in ['skincare', 'pet', 'beauty', 'home organization'] else 'Medium'
conversion_likelihood = 'Medium-High' if self.goal in ['ugc', 'conversion', 'affiliate'] else 'Medium'
return content_style, default_tier, audience_fit, conversion_likelihood
def render(self):
content_style, default_tier, audience_fit, conversion_likelihood = self.fit_analysis()
mode = GOAL_MODES[self.goal]
lines = [
'# Influencer Partnership Scout Brief', '',
f'**Campaign goal:** {self.goal}',
f'**Platform focus:** {self.platform}',
f'**Category:** {self.category}',
'**Method note:** This shortlist is heuristic and template-based. No real social platform data was accessed.', '',
'## Influencer Shortlist Framework',
f'- **High fit tier:** {self.tier} creators with strong {self.category} relevance and authentic {content_style}',
f'- **Test fit tier:** adjacent niche creators who can translate the product into a problem-solution or lifestyle use case',
f'- **Low priority tier:** large creators with weak audience overlap or overly generic sponsorship patterns', '',
'## Partnership Fit Analysis',
f'- Brand alignment: **High** if creator tone feels educational, believable, and consistent with {self.category}',
f'- Audience alignment: **{audience_fit}**',
f'- Content compatibility: **High** on {self.platform} for {content_style}',
f'- Conversion likelihood: **{conversion_likelihood}**',
f'- Suggested creator baseline: **{self.tier or default_tier}**', '',
'## Recommended Partnership Mode',
f'- Primary mode: **{mode}**',
'- Secondary mode: test one backup offer structure to compare response quality', '',
'## Contact Brief',
'- Opening angle: lead with why the product fits the creator’s audience and content rhythm',
'- What to mention first: category relevance, creator-audience fit, and the simplest partnership ask',
'- What to avoid: generic praise, vague CTA, and asking for a full campaign before validating fit', '',
'## Outreach Recommendation',
'1. Start with a small high-fit batch, not a broad list blast.',
'2. Separate gifting candidates from paid-post candidates.',
'3. Track reply quality, content authenticity, and audience resonance before scaling.', '',
'## Risk Notes'
]
for note in RISK_NOTES:
lines.append(f'- {note}')
return '\n'.join(lines)
def handle(user_input: str) -> str:
return ScoutEngine(user_input).render()
if __name__ == '__main__':
print(handle(sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()))
FILE:skill.json
{
"name": "Influencer Partnership Scout",
"slug": "influencer-partnership-scout",
"version": "1.0.0",
"description": "Builds influencer shortlist frameworks, fit analysis, and outreach strategy for ecommerce campaigns using heuristics.",
"author": "Golden Bean (OpenClaw)",
"tags": ["influencer", "creator", "ecommerce", "ugc", "affiliate", "campaign"],
"trigger_keywords": ["influencer", "creator", "outreach", "ugc", "affiliate", "kol", "koc"],
"language": "en",
"outputs": "markdown",
"requires_api": false,
"readiness": "stable"
}
FILE:tests/test_handler.py
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import ScoutEngine, handle
def test_platform_detection():
e = ScoutEngine('Skincare brand on Instagram with UGC goal')
assert e.platform == 'instagram'
print('✓ test_platform_detection passed')
def test_goal_detection():
e = ScoutEngine('Pet accessory launch for affiliate campaign on TikTok')
assert e.goal == 'affiliate'
print('✓ test_goal_detection passed')
def test_tier_detection():
e = ScoutEngine('Need micro creators 10k-80k on TikTok')
assert e.tier == 'micro'
print('✓ test_tier_detection passed')
def test_category_detection():
e = ScoutEngine('敏感肌护肤品牌 美国市场')
assert e.category == 'skincare'
print('✓ test_category_detection passed')
def test_markdown_output():
out = handle('Skincare brand on TikTok for UGC + conversion with micro creators')
assert out.startswith('# Influencer Partnership Scout Brief')
print('✓ test_markdown_output passed')
def test_contains_shortlist_and_outreach():
out = handle('Pet brand awareness campaign on Instagram')
assert '## Influencer Shortlist Framework' in out
assert '## Outreach Recommendation' in out
print('✓ test_contains_shortlist_and_outreach passed')
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Evaluates dropshipping products by scoring demand, competition, margin, creative fit, and risk to recommend go, test, or reject decisions.
# Dropshipping Product Research
## Overview
Dropshipping Product Research helps beginners and small operators evaluate whether a product is worth testing. It is a descriptive, non-API MVP focused on structured scoring, risk filtering, and clear go / test / reject decisions.
## Trigger
Use this skill when the user wants to:
- evaluate a product idea for dropshipping
- compare multiple product candidates
- estimate risk, margin, and creative fit
- build a weekly shortlist for testing
### Example prompts
- "Evaluate this product for dropshipping in the US"
- "Should I test a galaxy projector or pet grooming glove?"
- "Give me a go / test / reject recommendation"
- "Help me score 3 product ideas for my store"
## Workflow
1. Capture candidate, market, and positioning constraints.
2. Infer demand, competition, margin, creative angle, and risk.
3. Produce a viability score and recommendation.
4. Summarize why it may win, why it may fail, and what to test next.
## Inputs
- product name or keyword
- optional product link or niche
- target market
- price target or cost hints
- mode: single product / batch / trend scouting
## Outputs
- viability score
- sub-scores: demand, competition, margin, creative fit, risk
- recommendation: Go / Test / Reject
- memo with hypotheses and next steps
## Safety
- No marketplace scraping or real-time trend API access
- No guarantee of profit or compliance clearance
- Recommendations are heuristic and should be validated with real tests
## Acceptance Criteria
- Must output markdown
- Must include all five scoring dimensions
- Must include Go / Test / Reject recommendation
- Must include at least three risk or execution notes
FILE:ACCEPTANCE.md
# Acceptance Criteria
- Returns markdown memo
- Includes viability score and five scoring dimensions
- Includes Go / Test / Reject recommendation
- Includes at least three risk or execution notes
- Explicitly states heuristic / no real API access
FILE:handler.py
#!/usr/bin/env python3
import re
import sys
DEMAND_POSITIVE = ['pain', 'portable', 'gift', 'problem', 'solution', 'pet', 'office', 'organize', 'beauty', 'skincare', 'fitness', 'travel', 'home']
SATURATED = ['galaxy projector', 'posture corrector', 'led strip', 'fidget spinner', 'waist trainer']
HIGH_RISK = ['medical', 'supplement', 'battery', 'fragile', 'glass', 'cosmetic claim', 'copyright', 'trademark', 'baby', 'liquid']
CREATIVE_FRIENDLY = ['before after', 'demo', 'portable', 'pet', 'cleaning', 'organizer', 'relief', 'projector', 'beauty']
class ProductResearchEngine:
def __init__(self, text: str):
self.text = text.strip()
self.lower = self.text.lower()
self.market = self._extract_market()
self.price_low, self.price_high = self._extract_price_band()
self.product = self._extract_product()
def _extract_market(self):
for m in ['us', 'uk', 'germany', 'de', 'canada', 'australia', '美国', '英国', '德国']:
if m in self.lower:
return m.upper() if len(m) <= 3 else m.title()
return 'Generic'
def _extract_price_band(self):
nums = [int(n) for n in re.findall(r'\$?(\d{1,3})', self.lower)]
if len(nums) >= 2:
return min(nums[0], nums[1]), max(nums[0], nums[1])
if len(nums) == 1:
return nums[0], nums[0]
return 25, 60
def _extract_product(self):
words = re.split(r'[,\n]', self.text)
return words[0][:80] if words else self.text[:80]
def score_demand(self):
score = 70
if any(k in self.lower for k in DEMAND_POSITIVE):
score += 10
if 'seasonal' in self.lower or 'holiday' in self.lower:
score -= 10
return max(20, min(score, 95))
def score_competition(self):
score = 55
if any(k in self.lower for k in SATURATED):
score = 80
if 'niche' in self.lower or 'bundle' in self.lower:
score -= 10
return max(10, min(score, 95))
def score_margin(self):
low, high = self.price_low, self.price_high
midpoint = (low + high) / 2
if midpoint >= 35:
return 78
if midpoint >= 20:
return 65
return 45
def score_creative_fit(self):
score = 60
if any(k in self.lower for k in CREATIVE_FRIENDLY):
score += 15
if 'demo' in self.lower or 'ugc' in self.lower:
score += 10
return max(20, min(score, 95))
def score_risk(self):
score = 30
if any(k in self.lower for k in HIGH_RISK):
score += 35
if 'size' in self.lower or 'fragile' in self.lower:
score += 10
return max(5, min(score, 95))
def recommendation(self):
demand = self.score_demand()
comp = self.score_competition()
margin = self.score_margin()
creative = self.score_creative_fit()
risk = self.score_risk()
viability = round((demand + (100-comp) + margin + creative + (100-risk)) / 5)
if viability >= 72 and risk <= 45:
decision = 'Go'
elif viability >= 55:
decision = 'Test'
else:
decision = 'Reject'
return viability, decision
def render(self):
demand = self.score_demand()
comp = self.score_competition()
margin = self.score_margin()
creative = self.score_creative_fit()
risk = self.score_risk()
viability, decision = self.recommendation()
notes = [
'Validate unit economics with real supplier quotes before launch.',
'Use 3-5 ad angles before rejecting a product after one creative attempt.',
'Check compliance and claim language before publishing ads.',
]
why_win = 'Clear problem-solution framing and easy-to-demo content can improve test efficiency.'
why_fail = 'If the category is saturated or claims are hard to support, CAC and refund risk can rise quickly.'
lines = [
'# Dropshipping Product Research Memo', '',
f'**Candidate:** {self.product}',
f'**Target market:** {self.market}',
'**Method note:** Heuristic scoring only. No real-time marketplace or ad data was accessed.', '',
'## Viability Score',
f'- Overall viability: **{viability}/100**',
f'- Recommendation: **{decision}**', '',
'## Scored Analysis',
f'- Demand potential: **{demand}/100**',
f'- Competition saturation: **{comp}/100** (higher = more crowded)',
f'- Margin potential: **{margin}/100**',
f'- Creative angle potential: **{creative}/100**',
f'- Logistics / compliance risk: **{risk}/100** (higher = riskier)', '',
'## Research Memo',
f'- Why it may win: {why_win}',
f'- Why it may fail: {why_fail}',
'- Ideal angle: Focus on an obvious use-case, visual proof, and one memorable pain point.', '',
'## Go / Test / Reject Rationale',
f'- Decision summary: {decision} because viability is {viability}/100 with risk at {risk}/100.',
f'- Competition note: {"Highly saturated, differentiation needed." if comp >= 75 else "Competitive but still testable with angle discipline."}',
f'- Margin note: {"Healthy enough for paid testing." if margin >= 65 else "Margin looks thin, watch CAC carefully."}', '',
'## Risk & Execution Notes'
]
for n in notes:
lines.append(f'- {n}')
lines += ['', '## Next Steps', '1. Validate supplier cost, shipping time, and return risk.', '2. Prepare 3 ad hooks and 1 landing-page angle.', '3. Run a low-budget test before scaling.']
return '\n'.join(lines)
def handle(user_input: str) -> str:
return ProductResearchEngine(user_input).render()
if __name__ == '__main__':
print(handle(sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()))
FILE:skill.json
{
"name": "Dropshipping Product Research",
"slug": "dropshipping-product-research",
"version": "1.0.0",
"description": "Evaluates dropshipping product ideas with heuristic scoring across demand, competition, margin, creative fit, and risk.",
"author": "Golden Bean (OpenClaw)",
"tags": ["dropshipping", "product-research", "ecommerce", "product-selection", "scoring"],
"trigger_keywords": ["dropshipping", "product research", "go test reject", "product idea", "选品"],
"language": "en",
"outputs": "markdown",
"requires_api": false,
"readiness": "stable"
}
FILE:tests/test_handler.py
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import ProductResearchEngine, handle
def test_market_extraction():
e = ProductResearchEngine('portable neck stretcher, US market, price $29-$49')
assert e.market == 'US'
print('✓ test_market_extraction passed')
def test_price_band_extraction():
e = ProductResearchEngine('pet glove UK market $19-$35')
assert e.price_low == 19 and e.price_high == 35
print('✓ test_price_band_extraction passed')
def test_competition_saturated():
e = ProductResearchEngine('galaxy projector germany market')
assert e.score_competition() >= 75
print('✓ test_competition_saturated passed')
def test_risk_detection():
e = ProductResearchEngine('medical liquid product for baby')
assert e.score_risk() >= 60
print('✓ test_risk_detection passed')
def test_recommendation_present():
out = handle('portable pet grooming glove UK market $19-$35 demo content')
assert 'Recommendation: **' in out
print('✓ test_recommendation_present passed')
def test_output_has_all_dimensions():
out = handle('portable organizer US market $25-$49')
for key in ['Demand potential', 'Competition saturation', 'Margin potential', 'Creative angle potential', 'Logistics / compliance risk']:
assert key in out
print('✓ test_output_has_all_dimensions passed')
def test_output_markdown():
out = handle('neck stretcher US $29-$49')
assert out.startswith('# Dropshipping Product Research Memo')
print('✓ test_output_markdown passed')
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
Generates a heuristic markdown report mapping e-commerce customer journeys, identifying friction points, and prioritizing actionable improvements without rea...
# E-commerce Customer Journey Mapper
## Overview
E-commerce Customer Journey Mapper is a descriptive skill for DTC brands, Shopify operators, and e-commerce consultants. It synthesizes business context, customer touchpoints, reviews, FAQs, support signals, and optional funnel metrics into a stage-by-stage customer journey map.
This MVP does **not** call real analytics, heatmap, or CRM APIs. It uses built-in templates, stage heuristics, and best-practice libraries to generate a practical markdown brief.
## Trigger
Use this skill when the user wants to:
- diagnose conversion drop-off across the funnel
- map customer experience from awareness to retention
- align landing pages, ads, checkout, and retention messaging
- generate a quick journey audit for an e-commerce brand or client
### Example prompts
- "Map the customer journey for my skincare Shopify store"
- "Where are customers dropping off between ad click and checkout?"
- "Help me create a journey audit from reviews, FAQ, and landing page notes"
- "We have repeat purchase issues, can you map the weak stages?"
## Workflow
1. Collect business context, touchpoints, and analysis objective.
2. Normalize evidence into journey stages.
3. Detect likely friction points, emotion patterns, and gaps.
4. Score opportunities by impact, effort, and stage sensitivity.
5. Generate a markdown report with journey map, friction report, and action brief.
## Inputs
User can provide one or more of the following:
- store type / niche
- target market
- traffic sources
- touchpoint evidence: landing page notes, ads copy, email copy, reviews, FAQ, support logs
- optional funnel metrics
- analysis goal: drop-off diagnosis, checkout optimization, retention improvement, messaging alignment
- analysis mode: `quick` or `deep`
## Outputs
A markdown report with:
- Executive summary
- Journey stage map
- Friction point report
- Opportunity matrix
- Prioritized next-step brief
## Safety
- No real API calls or scraping
- No claims of factual analytics access
- Output quality depends on the materials provided by the user
- Recommendations are advisory, not guaranteed business outcomes
## Acceptance Criteria
- Must return markdown text
- Must cover at least 5 journey stages
- Must identify stage-specific friction points
- Must include prioritized recommendations
- Must clearly state that the analysis is heuristic and non-API-based
FILE:ACCEPTANCE.md
# Acceptance Criteria
- Returns markdown report
- Covers awareness through retention journey stages
- Includes friction point report and opportunity matrix
- Includes prioritized action brief
- Explicitly states heuristic / no real API access
FILE:handler.py
#!/usr/bin/env python3
import re
import sys
from typing import List, Dict, Tuple
STAGES = [
'Awareness', 'Consideration', 'Product Evaluation', 'Add-to-cart',
'Checkout', 'Post-purchase', 'Retention / Advocacy'
]
STAGE_RULES = {
'Awareness': ['ad', 'ads', 'meta', 'tiktok', 'instagram', 'traffic', 'click', '曝光', '广告', '点击', '流量'],
'Consideration': ['landing', 'homepage', 'message', 'messaging', 'faq', 'review', '疑虑', '页面', '文案', '评论'],
'Product Evaluation': ['product page', 'ingredient', 'comparison', 'proof', '评价', '产品页', '成分', '对比', '证明'],
'Add-to-cart': ['cart', '加购', 'shipping', '运费', 'bundle', '优惠'],
'Checkout': ['checkout', 'payment', '结账', '支付', 'address', 'tax'],
'Post-purchase': ['email', 'onboarding', 'delivery', 'support', '售后', '物流', '开箱', '使用指导'],
'Retention / Advocacy': ['repeat', 'retention', 'loyalty', 'referral', '复购', '留存', '会员', '推荐']
}
FRICTION_LIBRARY = {
'Awareness': ['Message promise is vague or too broad', 'Traffic source intent may not match landing-page promise'],
'Consideration': ['Key objections are not answered early enough', 'Trust proof is weak or scattered'],
'Product Evaluation': ['Product detail is insufficient for confident decision-making', 'Evidence does not fully support the promise'],
'Add-to-cart': ['Price/value framing may be unclear', 'Shipping or bundle incentives are not obvious'],
'Checkout': ['Late surprise costs create friction', 'Checkout flow may feel high-effort or low-trust'],
'Post-purchase': ['Onboarding and expectation-setting are weak', 'Support guidance is reactive instead of proactive'],
'Retention / Advocacy': ['No clear repeat-purchase trigger', 'Little post-purchase community or referral motivation']
}
RECOMMENDATIONS = {
'Awareness': ['Clarify primary value proposition in ads and above-the-fold copy', 'Align channel promise with landing-page proof'],
'Consideration': ['Move FAQ and objection handling higher on the page', 'Add social proof and category-specific reassurance blocks'],
'Product Evaluation': ['Improve comparison, ingredient/spec, and results framing', 'Add product-use context and expected-outcome guidance'],
'Add-to-cart': ['Test bundle framing and low-friction incentives', 'Surface shipping thresholds and return policy earlier'],
'Checkout': ['Reduce surprise fees and simplify field requirements', 'Increase visible trust signals around payment and shipping'],
'Post-purchase': ['Add onboarding emails and usage tips', 'Create a support macro for the top 3 confusion points'],
'Retention / Advocacy': ['Create a reorder or replenishment trigger', 'Add referral, review, or UGC follow-up flow']
}
class JourneyAnalyzer:
def __init__(self, text: str):
self.text = text.strip()
self.lower = self.text.lower()
self.mode = 'deep' if any(k in self.lower for k in ['deep', 'audit', 'full', '完整', '深度']) else 'quick'
self.goal = self._detect_goal()
self.matched = self._map_stages()
def _detect_goal(self) -> str:
goal_map = {
'retention': ['retention', 'repeat', '复购', '留存'],
'checkout': ['checkout', 'payment', '结账', '支付'],
'dropoff': ['drop', 'drop-off', 'conversion', '转化', '流失'],
'messaging': ['message', 'messaging', '文案', '信息一致'],
}
for goal, keys in goal_map.items():
if any(k in self.lower for k in keys):
return goal
return 'journey-audit'
def _map_stages(self) -> Dict[str, List[str]]:
matched = {}
for stage, keys in STAGE_RULES.items():
hits = [k for k in keys if k in self.lower]
matched[stage] = hits
return matched
def _evidence_summary(self, stage: str) -> str:
hits = self.matched.get(stage, [])
if hits:
return 'Signals found: ' + ', '.join(hits[:4])
return 'No direct signals found, using heuristic baseline from common e-commerce patterns.'
def _priority(self, stage: str) -> str:
if stage in ('Checkout', 'Add-to-cart', 'Product Evaluation'):
return 'High'
if stage in ('Consideration', 'Post-purchase'):
return 'Medium'
return 'Medium-Low'
def render(self) -> str:
lines = []
lines.append('# E-commerce Customer Journey Map')
lines.append('')
lines.append(f'**Mode:** {self.mode.title()}')
lines.append(f'**Primary objective:** {self.goal}')
lines.append('**Method note:** This is a heuristic, template-based analysis. No real API or analytics data was accessed.')
lines.append('')
lines.append('## Executive Summary')
lines.append('- This analysis focuses on aligning promise, proof, checkout clarity, and post-purchase continuity.')
lines.append('- The report prioritizes stages where friction most often suppresses conversion or repeat purchase.')
lines.append('')
lines.append('## Journey Stage Map')
for stage in STAGES:
lines.append(f'### {stage}')
lines.append(f'- Evidence: {self._evidence_summary(stage)}')
lines.append(f'- Likely friction: {FRICTION_LIBRARY[stage][0]}')
lines.append(f'- User emotion hypothesis: {"uncertain / skeptical" if stage in ("Consideration", "Product Evaluation", "Checkout") else "curious / evaluating"}')
lines.append(f'- Priority: {self._priority(stage)}')
lines.append('')
lines.append('## Friction Point Report')
for stage in STAGES:
lines.append(f'- **{stage}**: {FRICTION_LIBRARY[stage][0]}; {FRICTION_LIBRARY[stage][1]}')
lines.append('')
lines.append('## Opportunity Matrix')
lines.append('| Stage | Impact | Effort | Suggested move |')
lines.append('|---|---|---|---|')
for stage in ['Consideration', 'Product Evaluation', 'Checkout', 'Post-purchase']:
lines.append(f'| {stage} | High | Medium | {RECOMMENDATIONS[stage][0]} |')
lines.append('')
lines.append('## Prioritized Action Brief')
top = ['Consideration', 'Product Evaluation', 'Checkout'] if self.goal != 'retention' else ['Post-purchase', 'Retention / Advocacy', 'Product Evaluation']
for i, stage in enumerate(top, 1):
lines.append(f'{i}. **{stage}**: {RECOMMENDATIONS[stage][0]}')
lines.append(f' - Why now: {FRICTION_LIBRARY[stage][0]}')
lines.append(f' - Expected effect: Reduce uncertainty and increase stage progression')
return '\n'.join(lines)
def handle(user_input: str) -> str:
return JourneyAnalyzer(user_input).render()
if __name__ == '__main__':
print(handle(sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()))
FILE:skill.json
{
"name": "E-commerce Customer Journey Mapper",
"slug": "ecommerce-customer-journey-mapper",
"version": "1.0.0",
"description": "Maps ecommerce customer journey stages and outputs friction analysis plus prioritized action briefs using built-in heuristics.",
"author": "Golden Bean (OpenClaw)",
"tags": ["ecommerce", "customer-journey", "conversion", "funnel", "shopify", "dtc"],
"trigger_keywords": ["customer journey", "journey map", "conversion drop-off", "checkout friction", "retention audit"],
"language": "en",
"outputs": "markdown",
"requires_api": false,
"readiness": "stable"
}
FILE:tests/test_handler.py
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import JourneyAnalyzer, handle
def test_mode_detection():
a = JourneyAnalyzer('Please run a full audit for my checkout flow')
assert a.mode == 'deep'
print('✓ test_mode_detection passed')
def test_goal_detection_checkout():
a = JourneyAnalyzer('Our checkout completion is weak and payment confidence is low')
assert a.goal == 'checkout'
print('✓ test_goal_detection_checkout passed')
def test_goal_detection_retention():
a = JourneyAnalyzer('We have low repeat purchase and weak retention after first order')
assert a.goal == 'retention'
print('✓ test_goal_detection_retention passed')
def test_stage_mapping_awareness():
a = JourneyAnalyzer('Meta ads click-through is okay but landing page messaging is weak')
assert 'meta' in a.matched['Awareness']
print('✓ test_stage_mapping_awareness passed')
def test_stage_mapping_checkout():
a = JourneyAnalyzer('Shipping fees show too late during checkout payment step')
assert 'checkout' in a.matched['Checkout']
print('✓ test_stage_mapping_checkout passed')
def test_handle_returns_markdown():
out = handle('Map my ecommerce journey from ads to checkout')
assert out.startswith('# E-commerce Customer Journey Map')
assert '## Journey Stage Map' in out
print('✓ test_handle_returns_markdown passed')
def test_contains_five_plus_stages():
out = handle('journey audit for DTC brand')
assert out.count('### ') >= 5
print('✓ test_contains_five_plus_stages passed')
def test_contains_priority_actions():
out = handle('checkout dropoff analysis')
assert '## Prioritized Action Brief' in out
assert '1. **' in out
print('✓ test_contains_priority_actions passed')
if __name__ == '__main__':
for name, fn in list(globals().items()):
if name.startswith('test_') and callable(fn):
fn()
print('All tests passed.')
帮助社交电商运营系统规划内容主题、日历、直播脚本及短视频文案模板,提升选题与转化效果。
# Social Commerce Content Planner
## 1. Overview
**Name:** Social Commerce Content Planner
**Slug:** social-commerce-content-planner
**Type:** Descriptive content generation & planning skill
**Target Users:** 社交电商运营、直播带货主播、内容创作者,1-5人团队
帮助用户系统化地规划社交电商内容,包括:
- 趋势分析与热点捕捉
- 内容主题规划
- 创意生成与优化
- 内容日历排期
- 直播脚本与短视频文案模板
**Note:** 本技能聚焦描述性内容生成(模板、检查清单、最佳实践库),不接入实时API。
---
## 2. Trigger
触发方式(任选其一):
- 明确指令:`"规划本周内容"` / `"给我一个直播脚本模板"` / `"生成本月内容日历"`
- 场景关键词:`内容规划`、`直播脚本`、`带货文案`、`短视频主题`、`内容日历`、`电商趋势`
- 模糊需求:`我需要做电商内容`、`帮我规划下周直播`
---
## 3. Workflow
### Phase 1: 需求理解
- 识别用户是运营/主播/创作者身份
- 确认内容平台(抖音/快手/小红书/视频号/淘宝直播等)
- 确认行业品类(美妆/服饰/食品/家电等)
- 明确输出类型:主题/日历/脚本/文案/检查清单
### Phase 2: 趋势分析(描述性)
- 基于行业品类提供该领域的季节性趋势描述
- 给出节日营销节点建议(以年度节日历呈现)
- 提供该品类内容风格趋势(叙事型/测评型/对比型/故事型等)
### Phase 3: 内容规划
- 结合品类+平台+目标用户,生成内容主题列表
- 提供内容形式建议(短视频/图文/直播/种草笔记)
- 按周/月维度编排内容日历
- 为每个内容节点标注:目标-形式-核心卖点
### Phase 4: 创意与模板输出
- 生成具体脚本模板(直播逐字稿/口播文案/短视频分镜)
- 提供多种风格变体(专业型/亲和型/促销型/种草型)
- 输出内容检查清单(违禁词/合规性/转化路径)
### Phase 5: 最佳实践建议
- 各平台发布时间建议
- 互动率优化技巧
- 转化链路设计建议
- 数据复盘维度
---
## 4. Input / Output
### 典型输入
```
平台:抖音
品类:美妆护肤
目标用户:18-25岁女性
需求:本周内容主题 + 直播脚本模板
```
### 典型输出
- 本周内容主题清单(含选题方向)
- 内容日历(周维度,含每天内容类型+主题)
- 直播脚本模板(含开场/产品/促销/结尾结构)
- 短视频口播模板(3种风格)
- 内容自检清单
---
## 5. Safety
- 内容需符合各平台社区规范(禁止虚假宣传/绝对化用语/违禁词)
- 生成的营销文案需提醒用户自行核实信息来源
- 不生成医疗/药品/金融等特殊行业的夸大宣传内容
- 不接入实时数据,用户需自行核实节日/热点日期的准确性
---
## 6. Examples
### Example 1: 直播脚本生成
**Input:** 平台抖音,品类服装,需求女装夏季新款直播脚本
**Output:** 结构化直播逐字稿模板,含开场预热→产品介绍→试穿展示→限时促销→结尾引流各环节
### Example 2: 内容日历规划
**Input:** 平台小红书,品类母婴,需求6月内容日历
**Output:** 按周编排的内容日历,含主题/形式/发布时间/核心卖点
### Example 3: 短视频主题创意
**Input:** 品类食品-零食,需求10个短视频选题
**Output:** 分类选题列表(测评类/开箱类/剧情类/教程类),每个附创意方向说明
### Example 4: 种草文案模板
**Input:** 品类家电-空气炸锅,需求小红书种草笔记模板
**Output:** 多种风格的种草文案模板,含标题公式/正文结构/标签建议
---
## 7. Acceptance Criteria
- [ ] 给定品类+平台+需求,可输出完整内容规划方案
- [ ] 支持生成:主题列表、内容日历、直播脚本、短视频文案、种草笔记、检查清单
- [ ] 模板输出结构完整,包含各模块的占位说明
- [ ] 建议具有品类和平台的针对性(非通用废话)
- [ ] 自测脚本 `python3 handler.py` 可正常运行并输出有效JSON结果
- [ ] 测试用例 `python3 tests/test_handler.py` 全部通过
FILE:handler.py
#!/usr/bin/env python3
"""Social Commerce Content Planner - Handler"""
import json
import sys
import datetime
import calendar
# Seasonal trends data
SEASONAL_TRENDS = {
"美妆护肤": {
"春": ["敏感肌修护", "轻薄底妆", "春日限定色", "换季护肤routine"],
"夏": ["防晒防晒防晒", "控油持妆", "海边妆容", "晒后修复"],
"秋": ["秋冬滋润", "服帖底妆", "复古色调妆容", "护肤油使用"],
"冬": ["保湿大战", "年末节日妆", "暖色调妆容", "密集修护"]
},
"服饰": {
"春": ["轻薄外套", "衬衫叠穿", "早春色彩", "过敏季穿搭"],
"夏": ["清凉穿搭", "防晒衣", "海边度假风", "空调房披肩"],
"秋": ["层次感", "卫衣搭配", "复古街头", "秋色系搭配"],
"冬": ["羽绒服", "保暖内搭", "节日聚会装", "新年红色系"]
}
}
# Festival calendar
FESTIVAL_CALENDAR = [
{"month": 1, "name": "元旦", "days": "1月1日", "tag": "新年开年"},
{"month": 2, "name": "春节", "days": "2月中旬", "tag": "传统佳节"},
{"month": 3, "name": "妇女节", "days": "3月8日", "tag": "女性消费"},
{"month": 5, "name": "劳动节", "days": "5月1日", "tag": "促销旺季"},
{"month": 6, "name": "618", "days": "6月中旬", "tag": "年中大促"},
{"month": 11, "name": "双11", "days": "11月11日", "tag": "年度大促"},
{"month": 12, "name": "双12", "days": "12月12日", "tag": "年末冲刺"}
]
# Live script template
LIVE_SCRIPT_TEMPLATE = {
"title": "直播脚本模板(通用结构)",
"platforms": ["抖音", "快手", "淘宝直播", "视频号"],
"sections": [
{
"环节": "1. 开场预热(0-5分钟)",
"目的": "聚集人气,留住观众",
"内容要点": [
"打招呼,介绍自己/品牌",
"预告今日直播内容亮点",
"互动话术:欢迎进直播间的宝宝们,扣个666",
"发福袋/红包预热"
],
"话术示例": "欢迎宝宝们来到我的直播间!今天给大家带来一波超值的[品类]专场,记得看到最后,有重磅福利哦!"
},
{
"环节": "2. 痛点切入(5-10分钟)",
"目的": "引发共鸣,建立信任",
"内容要点": [
"描述目标用户的典型场景/痛点",
"提出解决方案的核心思路",
"引出今天的产品"
],
"话术示例": "是不是每次[痛点场景]?今天这款[产品名],就是专门解决这个问题的。"
}
]
}
# Content checklist
CONTENT_CHECKLIST = {
"合规检查": {
"违禁词": [
"国家级", "最高级", "最佳", "顶级", "顶尖",
"全网第一", "销量第一", "冠军", "遥遥领先",
"特效", "速效", "神效", "万能", "100%",
"保证治愈", "无效退款", "保险公司承保"
],
"绝对化用语": ["第一", "最好", "最强", "无敌", "终极"],
"虚假宣传风险": [
"未经证实的效果承诺",
"虚构用户评价",
"夸大原价/折扣力度"
]
}
}
def generate_content_topics(platform, category, count=10):
"""生成内容主题列表"""
base_topics = {
"美妆护肤": [
"开箱测评", "平替对比", "换季护肤", "妆教教程",
"产品回购", "踩雷吐槽", "成分分析", "空瓶记",
"618/双11好物", "明星同款"
],
"服饰": [
"穿搭分享", "一周穿搭", "平价替代", "显瘦秘籍",
"场合穿搭", "搭配技巧", "衣橱整理", "旧衣新穿",
"宝藏店铺", "配饰加分"
]
}
topics = base_topics.get(category, [
"产品测评", "使用教程", "好物分享", "选购指南",
"避坑指南", "对比体验", "真实反馈", "宝藏发现",
"送礼推荐", "使用技巧"
])[:count]
topic_details = []
for i, topic in enumerate(topics, 1):
topic_details.append({
"序号": i,
"主题": topic,
"内容形式": ["短视频", "图文"],
"创意方向": "真实分享+使用体验+推荐理由",
"时长建议": "30-90秒",
"tag建议": f"#{topic}# #{category}# #好物分享#"
})
return {
"platform": platform,
"category": category,
"total": len(topic_details),
"topics": topic_details
}
def generate_content_calendar(platform, category, month):
"""生成月度内容日历"""
cal = calendar.Calendar(firstweekday=0)
year = 2026 # Fixed year
weeks = list(cal.monthdatescalendar(year, month))
calendar_days = []
for week_idx, week in enumerate(weeks):
for day in week:
if day.month == month:
weekday = day.weekday()
weekday_names = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
if weekday in [5, 6]:
content_type = "短视频/种草"
elif weekday == 0:
content_type = "本周预告/福利"
else:
content_type = "产品展示/教程"
calendar_days.append({
"date": day.isoformat(),
"weekday": weekday_names[weekday],
"content_type": content_type,
"suggested_time": "12:00-13:00",
"theme": "日常更新",
"is_weekend": weekday in [5, 6]
})
return {
"month": month,
"platform": platform,
"category": category,
"calendar": calendar_days,
"festival_focus": [f for f in FESTIVAL_CALENDAR if f["month"] == month]
}
def handle(platform, category, request, month=None):
"""
处理内容规划请求
"""
result = {
"success": True,
"platform": platform,
"category": category,
"request": request,
"data": {}
}
# 趋势分析
season = _get_current_season()
seasonal = SEASONAL_TRENDS.get(category, {}).get(season, ["换季热点", "新品上市", "促销活动"])
result["data"]["trend_analysis"] = {
"current_season": season,
"seasonal_topics": seasonal,
"festival_calendar": [f for f in FESTIVAL_CALENDAR if f["month"] in [3, 4, 5, 6]][:3]
}
# 根据需求类型返回不同内容
if request in ["topic", "topics", "主题", "选题"]:
result["data"]["topics"] = generate_content_topics(platform, category)
elif request in ["calendar", "日历", "内容日历"]:
month = month or 4
result["data"]["calendar"] = generate_content_calendar(platform, category, month)
elif request in ["script", "直播脚本", "脚本"]:
result["data"]["live_script"] = LIVE_SCRIPT_TEMPLATE
elif request in ["checklist", "检查清单", "自检"]:
result["data"]["checklist"] = CONTENT_CHECKLIST
elif request in ["all", "全部", "完整"]:
month = month or 4
result["data"] = {
"trend_analysis": result["data"]["trend_analysis"],
"topics": generate_content_topics(platform, category),
"calendar": generate_content_calendar(platform, category, month),
"live_script": LIVE_SCRIPT_TEMPLATE,
"checklist": CONTENT_CHECKLIST
}
else:
result["success"] = False
result["error"] = f"未知的请求类型: {request},支持的类型: topic/calendar/script/checklist/all"
return result
def _get_current_season():
"""获取当前季节"""
month = datetime.datetime.now().month
if month in [3, 4, 5]:
return "春"
elif month in [6, 7, 8]:
return "夏"
elif month in [9, 10, 11]:
return "秋"
else:
return "冬"
# ============================================================
# 自测入口
# ============================================================
if __name__ == "__main__":
print("=" * 60)
print("Social Commerce Content Planner - 自测")
print("=" * 60)
print("\n[测试1] 生成内容主题")
r1 = handle("抖音", "美妆护肤", "topic")
print(f"平台: {r1['platform']}, 品类: {r1['category']}")
print(f"生成主题数: {r1['data']['topics']['total']}")
print(f"第一个主题: {r1['data']['topics']['topics'][0]['主题']}")
print("\n[测试2] 生成内容日历")
r2 = handle("小红书", "服饰", "calendar", month=6)
print(f"平台: {r2['platform']}, 品类: {r2['category']}, 月份: 6月")
print(f"日历天数: {len(r2['data']['calendar']['calendar'])}")
print("\n[测试3] 获取直播脚本模板")
r3 = handle("抖音", "服饰", "script")
print(f"直播环节数: {len(r3['data']['live_script']['sections'])}")
print("\n[测试4] 获取内容检查清单")
r4 = handle("快手", "美妆护肤", "checklist")
print(f"合规检查项: {len(r4['data']['checklist']['合规检查']['违禁词'])}")
print("\n[测试5] 完整内容包")
r5 = handle("抖音", "美妆护肤", "all")
print(f"完整包包含模块: {list(r5['data'].keys())}")
print("\n" + "=" * 60)
print("自测完成!所有功能正常运行。")
print("=" * 60)
FILE:skill.json
{
"name": "Social Commerce Content Planner",
"slug": "social-commerce-content-planner",
"version": "1.0.0",
"description": "社交电商内容规划器:趋势分析、内容主题规划、创意生成、内容日历、直播脚本与短视频文案模板",
"category": "ecommerce",
"tags": [
"社交电商",
"内容规划",
"直播脚本",
"短视频文案",
"内容日历",
"电商运营",
"直播带货"
],
"author": "Harry",
"language": "zh-CN",
"platform": "all",
"input": {
"platform": "string (抖音/快手/小红书/视频号/淘宝直播)",
"category": "string (美妆护肤/服饰/食品/家电/母婴/3C数码/宠物)",
"request": "string (topic/calendar/script/template/checklist/xhs/all)",
"month": "number (可选,用于日历生成)"
},
"output": {
"format": "JSON",
"structure": {
"success": "boolean",
"platform": "string",
"category": "string",
"request": "string",
"data": "object (根据request类型变化)"
}
},
"dependencies": [],
"runtime": "python3",
"skillhub": false,
"clawhub": false
}
FILE:tests/test_handler.py
#!/usr/bin/env python3
"""
Social Commerce Content Planner - 测试用例
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from handler import (
handle,
generate_content_topics,
generate_content_calendar,
SEASONAL_TRENDS,
FESTIVAL_CALENDAR,
LIVE_SCRIPT_TEMPLATE,
CONTENT_CHECKLIST
)
def test_handle_topic():
"""测试主题生成功能"""
result = handle("抖音", "美妆护肤", "topic")
assert result["success"] == True
assert result["platform"] == "抖音"
assert result["category"] == "美妆护肤"
assert "topics" in result["data"]
assert result["data"]["topics"]["total"] > 0
assert len(result["data"]["topics"]["topics"]) > 0
first_topic = result["data"]["topics"]["topics"][0]
assert "主题" in first_topic
assert "内容形式" in first_topic
print("✓ test_handle_topic 通过")
def test_handle_calendar():
"""测试内容日历生成"""
result = handle("小红书", "服饰", "calendar", month=6)
assert result["success"] == True
assert "calendar" in result["data"]
assert result["data"]["calendar"]["month"] == 6
assert len(result["data"]["calendar"]["calendar"]) > 0
print("✓ test_handle_calendar 通过")
def test_handle_script():
"""测试直播脚本模板"""
result = handle("抖音", "服饰", "script")
assert result["success"] == True
assert "live_script" in result["data"]
assert "sections" in result["data"]["live_script"]
assert len(result["data"]["live_script"]["sections"]) > 0
first_section = result["data"]["live_script"]["sections"][0]
assert "环节" in first_section
assert "目的" in first_section
print("✓ test_handle_script 通过")
def test_handle_checklist():
"""测试内容检查清单"""
result = handle("快手", "美妆护肤", "checklist")
assert result["success"] == True
assert "checklist" in result["data"]
assert "合规检查" in result["data"]["checklist"]
assert len(result["data"]["checklist"]["合规检查"]["违禁词"]) > 0
print("✓ test_handle_checklist 通过")
def test_handle_all():
"""测试完整内容包"""
result = handle("抖音", "美妆护肤", "all")
assert result["success"] == True
expected_keys = ["trend_analysis", "topics", "calendar", "live_script", "checklist"]
for key in expected_keys:
assert key in result["data"], f"缺少模块: {key}"
print("✓ test_handle_all 通过")
def test_generate_content_topics():
"""测试内容主题生成函数"""
result = generate_content_topics("抖音", "美妆护肤", count=5)
assert result["platform"] == "抖音"
assert result["category"] == "美妆护肤"
assert result["total"] == 5
assert len(result["topics"]) == 5
print("✓ test_generate_content_topics 通过")
def test_generate_content_calendar():
"""测试内容日历生成函数"""
result = generate_content_calendar("小红书", "服饰", month=6)
assert result["month"] == 6
assert result["platform"] == "小红书"
assert result["category"] == "服饰"
assert len(result["calendar"]) > 0
has_weekend = any(day["is_weekend"] for day in result["calendar"])
assert has_weekend
print("✓ test_generate_content_calendar 通过")
def test_trend_analysis():
"""测试趋势分析数据"""
assert "美妆护肤" in SEASONAL_TRENDS
assert "春" in SEASONAL_TRENDS["美妆护肤"]
assert len(SEASONAL_TRENDS["美妆护肤"]["春"]) > 0
assert len(FESTIVAL_CALENDAR) > 0
months = [f["month"] for f in FESTIVAL_CALENDAR]
assert 1 in months
assert 11 in months
print("✓ test_trend_analysis 通过")
def run_all_tests():
"""运行所有测试"""
print("=" * 60)
print("Social Commerce Content Planner - 测试套件")
print("=" * 60)
print()
tests = [
test_handle_topic,
test_handle_calendar,
test_handle_script,
test_handle_checklist,
test_handle_all,
test_generate_content_topics,
test_generate_content_calendar,
test_trend_analysis,
]
passed = 0
failed = 0
for test in tests:
try:
test()
passed += 1
except AssertionError as e:
print(f"✗ {test.__name__} 失败: {e}")
failed += 1
except Exception as e:
print(f"✗ {test.__name__} 异常: {e}")
failed += 1
print()
print("=" * 60)
print(f"测试结果: {passed} 通过, {failed} 失败")
print("=" * 60)
return failed == 0
if __name__ == "__main__":
success = run_all_tests()
sys.exit(0 if success else 1)
为中阶 Amazon 卖家提供 Listing 健康度诊断、关键词分析、竞品对标及结构化优化建议,助力提升销售表现。
# Amazon Listing Analyzer
## 1. Overview
Amazon Listing Analyzer 为 Amazon 卖家提供 Listing 健康度诊断、关键词研究和竞品对标分析,输出结构化的优化建议包。面向月销售额 $5k–$200k、1–5 年运营经验的中阶卖家。不依赖实时 API,所有数据基于内置规则库和模板生成。
## 2. Trigger
用户通过对话发起以下类型的分析请求:
- "分析这个 Listing:..."
- "帮我检查这个 Amazon Listing 的健康度"
- "关键词分析:[产品名称/类目]"
- "竞品对标:[ASIN 或产品描述]"
- "给我一个 Amazon Listing 优化建议"
## 3. Workflow
```
用户输入 → 解析意图(健康度评分 | 关键词分析 | 竞品对标 | 优化建议包)
→ 调用对应分析模块
→ 聚合结果 → 输出结构化报告
```
### 3.1 Listing 健康度评分
1. 解析标题、五点、描述、Search Terms、Backend Keywords
2. 按以下维度打分(每项 0-100):
- 标题质量(长度、关键词、前缀品牌词)
- 五点描述(数量、长度、特征覆盖)
- 描述质量(结构化程度、可读性)
- 图片描述(Alt 文本覆盖)
- 关键词填充(无重复、合理密度)
- 合规性检查(禁止词、过敏词)
3. 综合得分 = 加权平均
4. 输出诊断结论 + 分项问题列表
### 3.2 关键词分析
1. 基于产品信息生成种子关键词列表
2. 对每个关键词从内置词库查询:
- 搜索量等级(High/Medium/Low/Unknown)
- 竞争度等级(High/Medium/Low/Unknown)
- 相关性评级(1-5)
3. 输出关键词矩阵表 + 建议优先词列表
### 3.3 竞品对标分析
1. 输入竞品 ASIN 或产品描述
2. 从内置竞品模板库匹配相似产品
3. 对比维度:标题结构、价格区间、评分分布、评论数、核心卖点
4. 输出对标表 + 差异化机会点
### 3.4 优化建议包
1. 综合健康度评分 + 关键词分析 + 竞品对标
2. 生成结构化建议:
- 标题优化建议
- 五点描述优化建议
- 描述优化建议
- 关键词补全建议
- 图片建议清单
3. 按优先级排序输出
## 4. I/O Specification
### 输入(JSON dict 或对话文本)
```json
{
"intent": "health_score | keyword_analysis | competitor_benchmark | full_optimization",
"product_title": "string (optional)",
"bullet_points": ["string"] * 5 (optional)",
"product_description": "string (optional)",
"search_terms": "string (optional)",
"backend_keywords": "string (optional)",
"competitor_asin": "string (optional)",
"product_category": "string (optional)",
"product_features": ["string"] (optional)"
}
```
### 输出(JSON dict)
```json
{
"status": "success | partial | error",
"module": "string",
"result": {
"health_score": {
"total": 0-100,
"dimensions": {
"title": {"score": 0-100, "issues": []},
"bullets": {"score": 0-100, "issues": []},
"description": {"score": 0-100, "issues": []},
"keywords": {"score": 0-100, "issues": []},
"compliance": {"score": 0-100, "issues": []}
},
"summary": "string"
},
"keyword_analysis": {
"matrix": [
{"keyword": "string", "volume": "string", "competition": "string", "relevance": 1-5}
],
"priority_keywords": ["string"],
"long_tail_keywords": ["string"]
},
"competitor_benchmark": {
"comparisons": [
{"dimension": "string", "you": "string", "competitor": "string", "opportunity": "string"}
],
"gaps": ["string"]
},
"optimization_package": {
"title": {"current": "string", "suggested": "string", "priority": "high|medium|low"},
"bullets": [{"current": "string", "suggested": "string", "priority": "string"}],
"description": {"current": "string", "suggested": "string", "priority": "string"},
"keywords": {"missing": [], "redundant": [], "suggested": []}
}
},
"errors": ["string"] (optional)
}
```
## 5. Safety
- 不请求或存储用户的真实 Amazon 账户凭证
- 不调用任何外部 API(数据来自内置规则库)
- 所有分析输出为参考建议,不构成 Amazon 平台合规承诺
- 输入文本进行基础长度校验,拒绝超长输入(>10,000 字符)
- 不处理任何涉及个人信息的内容
## 6. Examples
### Example 1: 健康度评分
**输入:**
```json
{"intent": "health_score", "product_title": "Premium Wireless Bluetooth Headphones with Noise Cancellation", "bullet_points": ["High quality sound", "30-hour battery life", "Comfortable fit", "Fast charging", "Foldable design"], "product_description": "Experience music like never before...", "search_terms": "wireless headphones bluetooth noise cancellation"}
```
**输出:**
```json
{
"status": "success",
"module": "health_score",
"result": {
"health_score": {
"total": 72,
"dimensions": {
"title": {"score": 75, "issues": ["缺少核心关键词搜索量验证", "品牌词位置偏后"]},
"bullets": {"score": 70, "issues": ["卖点不够具体,缺少数据支撑"]},
"description": {"score": 68, "issues": ["缺少品牌故事和使用场景描述"]},
"keywords": {"score": 78, "issues": ["Search Terms 未充分利用"]},
"compliance": {"score": 90, "issues": []}
},
"summary": "Listing 健康度中等偏上,主要改进空间在标题关键词精准度和五点描述的具体性。"
}
}
}
```
### Example 2: 关键词分析
**输入:**
```json
{"intent": "keyword_analysis", "product_category": "Electronics > Headphones", "product_features": ["wireless", "noise cancellation", "bluetooth", "long battery life", "comfortable"]}
```
**输出:**
```json
{
"status": "success",
"module": "keyword_analysis",
"result": {
"keyword_analysis": {
"matrix": [
{"keyword": "wireless headphones", "volume": "High", "competition": "High", "relevance": 5},
{"keyword": "bluetooth headphones", "volume": "High", "competition": "High", "relevance": 5},
{"keyword": "noise cancelling headphones", "volume": "High", "competition": "Medium", "relevance": 4},
{"keyword": "long battery life headphones", "volume": "Medium", "competition": "Low", "relevance": 4},
{"keyword": "comfortable headphones", "volume": "Medium", "competition": "Medium", "relevance": 3}
],
"priority_keywords": ["wireless headphones", "bluetooth headphones", "noise cancelling headphones"],
"long_tail_keywords": ["long battery life wireless headphones", "comfortable noise cancelling headphones"]
}
}
}
```
## 7. Acceptance Criteria
1. **SKILL.md 完整** — 包含 Overview/Trigger/Workflow/I/O/Safety/Examples/Acceptance 全部 7 个模块
2. **handler.py 可独立运行** — `python3 handler.py` 直接执行并输出有效 JSON 结果
3. **测试通过** — `python3 tests/test_handler.py` 至少 3 个测试用例全部通过
4. **元数据完整** — `skill.json` 和 `.claw/identity.json` 字段齐全
5. **无实时 API 依赖** — 所有数据来自内置规则库和模板
6. **输入校验** — 拒绝超长输入(>10,000 字符)并返回错误
7. **输出格式一致** — 所有模块返回统一 JSON 结构
FILE:handler.py
#!/usr/bin/env python3
"""
Amazon Listing Analyzer - handler.py
不依赖实时API,所有数据来自内置规则库和模板。
"""
import json
import re
from typing import Dict, List, Any, Optional
# ============================================================
# 内置数据:关键词数据库(模拟,非实时数据)
# ============================================================
KEYWORD_DB: Dict[str, Dict[str, str]] = {
"wireless headphones": {"volume": "High", "competition": "High", "relevance": "5"},
"bluetooth headphones": {"volume": "High", "competition": "High", "relevance": "5"},
"noise cancelling headphones": {"volume": "High", "competition": "Medium", "relevance": "4"},
"noise cancellation headphones": {"volume": "High", "competition": "Medium", "relevance": "4"},
"headphones": {"volume": "High", "competition": "High", "relevance": "5"},
"earphones": {"volume": "High", "competition": "High", "relevance": "4"},
"long battery life headphones": {"volume": "Medium", "competition": "Low", "relevance": "4"},
"comfortable headphones": {"volume": "Medium", "competition": "Medium", "relevance": "3"},
"gaming headphones": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"sports headphones": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"waterproof headphones": {"volume": "Medium", "competition": "Low", "relevance": "4"},
"over ear headphones": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"on ear headphones": {"volume": "Medium", "competition": "Medium", "relevance": "3"},
"portable speakers": {"volume": "High", "competition": "High", "relevance": "5"},
"bluetooth speaker": {"volume": "High", "competition": "High", "relevance": "5"},
"smart watch": {"volume": "High", "competition": "High", "relevance": "5"},
"fitness tracker": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"running shoes": {"volume": "High", "competition": "High", "relevance": "5"},
"casual shoes": {"volume": "High", "competition": "High", "relevance": "4"},
"laptop stand": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"phone case": {"volume": "High", "competition": "High", "relevance": "5"},
"charging cable": {"volume": "High", "competition": "High", "relevance": "5"},
"usb cable": {"volume": "High", "competition": "High", "relevance": "5"},
"power bank": {"volume": "High", "competition": "High", "relevance": "5"},
"external battery": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"wireless charger": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"desk lamp": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"led lamp": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"kitchen knife": {"volume": "Medium", "competition": "Medium", "relevance": "4"},
"cooking pot": {"volume": "Medium", "competition": "Low", "relevance": "4"},
}
# 禁止词/过敏词库
FORBIDDEN_WORDS = [
"fake", "replica", "knockoff", "counterfeit", "clone",
"miracle", "cure", "treats", "heals", "prevents disease",
".weight loss", "lose weight", "burn fat",
"best", "top rated", "number 1", "#1", "most popular",
]
# 竞品模板库
COMPETITOR_TEMPLATES = {
"B00XXXXXXXX": {
"title": "Sony WH-1000XM5 Wireless Noise Cancelling Headphones",
"price_range": "$300-$350", "rating": "4.7", "reviews": "50000+",
"core_features": ["Industry-leading noise cancellation", "30hr battery", "Multipoint connection"],
},
"B0BYXXXXXXXX": {
"title": "Apple AirPods Pro 2nd Gen",
"price_range": "$220-$250", "rating": "4.8", "reviews": "100000+",
"core_features": ["Active noise cancellation", "Adaptive transparency", "Personalized spatial audio"],
},
"B09JQMJXYZ": {
"title": "Bose QuietComfort 45",
"price_range": "$270-$300", "rating": "4.6", "reviews": "30000+",
"core_features": ["QuietComfort technology", "24hr battery", "Lightweight design"],
},
}
# ============================================================
# 核心分析模块
# ============================================================
def score_title(title: str) -> Dict[str, Any]:
"""评估标题质量"""
issues = []
score = 80 # 基准分
if not title:
return {"score": 0, "issues": ["标题为空"]}
title_len = len(title)
if title_len < 50:
score -= 20
issues.append("标题过短(<50字符),建议80-200字符")
elif title_len > 200:
score -= 10
issues.append("标题过长(>200字符),可能被截断")
words = title.split()
if len(words) < 5:
score -= 15
issues.append("标题词数过少")
# 品牌词检测
if title and title[0].isupper() and len(title.split()[0]) > 2:
pass # 首词大写通常是品牌词,正常
else:
score -= 5
issues.append("标题可能缺少品牌词或品牌词位置不规范")
# 关键词覆盖(简单检测)
title_lower = title.lower()
if "headphone" not in title_lower and "earphone" not in title_lower and "earbud" not in title_lower:
score -= 10
issues.append("标题缺少产品核心词(headphone/earphone/earbud)")
score = max(0, min(100, score))
return {"score": score, "issues": issues}
def score_bullets(bullets: List[str]) -> Dict[str, Any]:
"""评估五点描述质量"""
issues = []
score = 80
if not bullets:
return {"score": 0, "issues": ["五点描述为空"]}
count = len(bullets)
if count < 5:
score -= (5 - count) * 8
issues.append(f"五点描述不完整({count}/5)")
for i, bullet in enumerate(bullets):
blen = len(bullet)
if blen < 20:
score -= 3
issues.append(f"第{i+1}条卖点过短(<20字符),缺乏信息量")
if blen > 500:
score -= 5
issues.append(f"第{i+1}条卖点过长(>500字符)")
score = max(0, min(100, score))
return {"score": score, "issues": issues}
def score_description(description: str) -> Dict[str, Any]:
"""评估产品描述质量"""
issues = []
score = 70
if not description:
return {"score": 0, "issues": ["产品描述为空"]}
dlen = len(description)
if dlen < 100:
score -= 15
issues.append("描述过短(<100字符),缺少品牌故事和使用场景")
elif dlen > 2000:
score -= 5
issues.append("描述过长(>2000字符),可能包含过多冗余信息")
# 段落结构检测
sentences = re.split(r'[.\n]', description)
if len(sentences) < 3:
score -= 10
issues.append("描述缺少段落结构,建议分段说明产品特点和使用场景")
score = max(0, min(100, score))
return {"score": score, "issues": issues}
def score_keywords(search_terms: str, backend: str) -> Dict[str, Any]:
"""评估关键词填充"""
issues = []
score = 75
total = (search_terms or "") + (backend or "")
if not total.strip():
return {"score": 0, "issues": ["Search Terms 和 Backend Keywords 均为空"]}
words = total.split()
word_count = len(words)
if word_count < 5:
score -= 15
issues.append("Search Terms 关键词数量不足(<5个)")
elif word_count > 250:
score -= 10
issues.append("Backend Keywords 词数过多(>250)")
# 重复检测
seen = set()
duplicates = []
for w in words[:50]: # 只检查前50个词
wl = w.lower()
if wl in seen:
duplicates.append(w)
seen.add(wl)
if duplicates:
score -= 5
issues.append(f"存在重复关键词: {', '.join(set(duplicates))}")
score = max(0, min(100, score))
return {"score": score, "issues": issues}
def score_compliance(title: str, bullets: List[str], description: str) -> Dict[str, Any]:
"""合规性检查"""
issues = []
score = 100
all_text = title + " " + " ".join(bullets) + " " + description
all_lower = all_text.lower()
for word in FORBIDDEN_WORDS:
if word in all_lower:
score -= 15
issues.append(f"发现禁止词/违规词: '{word}'")
score = max(0, min(100, score))
return {"score": score, "issues": issues}
def health_score(input_data: Dict) -> Dict:
"""Listing健康度评分"""
title = input_data.get("product_title", "")
bullets = input_data.get("bullet_points", [])
description = input_data.get("product_description", "")
search_terms = input_data.get("search_terms", "")
backend = input_data.get("backend_keywords", "")
title_result = score_title(title)
bullets_result = score_bullets(bullets)
desc_result = score_description(description)
kw_result = score_keywords(search_terms, backend)
comp_result = score_compliance(title, bullets, description)
# 加权平均
weights = {"title": 0.25, "bullets": 0.25, "description": 0.20, "keywords": 0.15, "compliance": 0.15}
total = (
title_result["score"] * weights["title"] +
bullets_result["score"] * weights["bullets"] +
desc_result["score"] * weights["description"] +
kw_result["score"] * weights["keywords"] +
comp_result["score"] * weights["compliance"]
)
total = round(total)
if total >= 80:
summary = "Listing 健康度良好,各项指标达标,继续保持优化节奏。"
elif total >= 60:
summary = "Listing 健康度中等偏上,主要改进空间在标题关键词精准度和五点描述的具体性。"
elif total >= 40:
summary = "Listing 健康度偏低,需要系统性优化标题、五点描述和关键词布局。"
else:
summary = "Listing 健康度较差,建议优先完善基础信息(标题+五点),再逐步优化其他维度。"
return {
"total": total,
"dimensions": {
"title": title_result,
"bullets": bullets_result,
"description": desc_result,
"keywords": kw_result,
"compliance": comp_result,
},
"summary": summary,
}
def keyword_analysis(input_data: Dict) -> Dict:
"""关键词分析"""
features = input_data.get("product_features", [])
category = input_data.get("product_category", "")
title = input_data.get("product_title", "")
bullets = input_data.get("bullet_points", [])
# 生成种子关键词
seed_keywords = set()
# 从features提取
for f in features:
seed_keywords.add(f.lower().strip())
if "headphone" in f.lower() or "speaker" in f.lower() or "watch" in f.lower():
seed_keywords.add(f"{f.lower()}".replace(" ", " ").strip())
# 从category提取核心词
if category:
cat_words = re.findall(r'\b\w+\b', category.lower())
for w in cat_words:
if len(w) > 3:
seed_keywords.add(w)
# 从title/bullets提取
all_text = title + " " + " ".join(bullets)
for word in re.findall(r'\b[a-z]+\b', all_text.lower()):
if len(word) > 3:
seed_keywords.add(word)
# 查询内置DB
matrix = []
priority = []
long_tail = []
for kw in sorted(seed_keywords):
if kw in KEYWORD_DB:
info = KEYWORD_DB[kw]
matrix.append({
"keyword": kw,
"volume": info["volume"],
"competition": info["competition"],
"relevance": int(info["relevance"]),
})
if int(info["relevance"]) >= 4 and info["volume"] in ("High", "Medium"):
priority.append(kw)
else:
# 模糊匹配
for db_kw, info in KEYWORD_DB.items():
if kw in db_kw or db_kw in kw:
matrix.append({
"keyword": db_kw,
"volume": info["volume"],
"competition": info["competition"],
"relevance": int(info["relevance"]) - 1,
})
break
else:
matrix.append({
"keyword": kw,
"volume": "Unknown",
"competition": "Unknown",
"relevance": 2,
})
# 生成long-tail
if len(priority) >= 2:
long_tail.append(f"{priority[0]} {priority[1]}")
if len(features) >= 1:
long_tail.append(f"{features[0].lower()} headphones")
# 去重
priority = list(dict.fromkeys(priority))[:10]
matrix = matrix[:20]
return {
"matrix": matrix,
"priority_keywords": priority,
"long_tail_keywords": long_tail,
}
def competitor_benchmark(input_data: Dict) -> Dict:
"""竞品对标分析"""
competitor_asin = input_data.get("competitor_asin", "")
product_title = input_data.get("product_title", "")
bullets = input_data.get("bullet_points", [])
# 匹配竞品
competitor = COMPETITOR_TEMPLATES.get(competitor_asin, {
"title": "某头部竞品(ASIN: " + competitor_asin + ")",
"price_range": "$50-$200",
"rating": "4.5",
"reviews": "10000+",
"core_features": ["知名品牌", "稳定评分", "大量真实评论"],
})
comparisons = [
{
"dimension": "标题结构",
"you": product_title[:60] + "..." if len(product_title) > 60 else product_title,
"competitor": competitor["title"],
"opportunity": "参考竞品标题结构:品牌+核心词+特性+型号",
},
{
"dimension": "价格区间",
"you": "待定价(未提供)",
"competitor": competitor["price_range"],
"opportunity": "建议定价参考竞品区间,结合成本和毛利确定",
},
{
"dimension": "评分",
"you": "待积累(新品或无评分)",
"competitor": f"{competitor['rating']} ({competitor['reviews']} 条评论)",
"opportunity": "提升产品力和服务,争取更多高质量评论",
},
{
"dimension": "核心卖点",
"you": "; ".join(bullets[:3]) if bullets else "待完善",
"competitor": "; ".join(competitor["core_features"][:3]),
"opportunity": "提炼差异化卖点,避免同质化",
},
]
gaps = [
"缺少竞品评分和评论数对比数据(需手动调研)",
"差异化卖点不够突出,建议结合用户痛点提炼",
"标题关键词覆盖不如竞品全面",
]
return {
"comparisons": comparisons,
"gaps": gaps,
}
def full_optimization(input_data: Dict) -> Dict:
"""生成完整优化建议包"""
health = health_score(input_data)
keywords = keyword_analysis(input_data)
title = input_data.get("product_title", "")
bullets = input_data.get("bullet_points", [])
description = input_data.get("product_description", "")
# 标题优化
title_suggested = title
title_priority = "high"
if health["dimensions"]["title"]["score"] < 75:
title_suggested = f"{title} - Wireless, Premium Sound, Comfortable Fit" if title else "[品牌词] [核心产品词] [特性词] [型号/规格]"
title_priority = "high"
elif health["dimensions"]["title"]["score"] < 90:
title_suggested = f"{title} [增强关键词]" if title else title
title_priority = "medium"
else:
title_priority = "low"
# 五点优化
bullet_suggestions = []
for i, b in enumerate(bullets):
suggested = b
if len(b) < 30:
suggested = f"{b} - [补充具体数据和功效描述]"
bullet_suggestions.append({
"current": b,
"suggested": suggested,
"priority": "high" if len(b) < 30 else "medium",
})
while len(bullet_suggestions) < 5:
bullet_suggestions.append({
"current": "",
"suggested": "[新增卖点:具体化数字/功效/场景]",
"priority": "high",
})
# 描述优化
desc_suggested = description
desc_priority = "low"
if health["dimensions"]["description"]["score"] < 70:
desc_suggested = (
f"{description}\n\n"
"[品牌故事段落]\n"
"[使用场景描述]\n"
"[产品保修/售后信息]"
)
desc_priority = "high"
# 关键词建议
missing = [kw["keyword"] for kw in keywords["matrix"] if kw["volume"] == "Unknown"]
redundant = []
suggested_kws = keywords["priority_keywords"][:5]
return {
"title": {"current": title, "suggested": title_suggested, "priority": title_priority},
"bullets": bullet_suggestions,
"description": {"current": description, "suggested": desc_suggested, "priority": desc_priority},
"keywords": {
"missing": missing[:5],
"redundant": redundant,
"suggested": suggested_kws,
},
}
# ============================================================
# 入口函数
# ============================================================
def analyze(input_data: Dict[str, Any]) -> Dict[str, Any]:
"""
主分析入口,根据 intent 分派到对应模块。
"""
# 输入长度校验
raw_str = json.dumps(input_data, ensure_ascii=False)
if len(raw_str) > 10000:
return {
"status": "error",
"module": "input_validation",
"result": {},
"errors": [f"输入数据过长({len(raw_str)}字符),超过10000字符限制"],
}
intent = input_data.get("intent", "")
if intent == "health_score":
module = "health_score"
result = {"health_score": health_score(input_data)}
elif intent == "keyword_analysis":
module = "keyword_analysis"
result = {"keyword_analysis": keyword_analysis(input_data)}
elif intent == "competitor_benchmark":
module = "competitor_benchmark"
result = {"competitor_benchmark": competitor_benchmark(input_data)}
elif intent == "full_optimization":
module = "full_optimization"
result = {
"health_score": health_score(input_data),
"keyword_analysis": keyword_analysis(input_data),
"competitor_benchmark": competitor_benchmark(input_data),
"optimization_package": full_optimization(input_data),
}
else:
# 默认执行健康度评分
module = "health_score"
result = {"health_score": health_score(input_data)}
return {
"status": "success",
"module": module,
"result": result,
}
# ============================================================
# CLI 入口(自测)
# ============================================================
if __name__ == "__main__":
print("=" * 60)
print("Amazon Listing Analyzer - 自测模式")
print("=" * 60)
# 测试用例1:健康度评分
print("\n[Test 1] 健康度评分")
test1 = {
"intent": "health_score",
"product_title": "Premium Wireless Bluetooth Headphones with Noise Cancellation",
"bullet_points": [
"High quality sound",
"30-hour battery life",
"Comfortable fit",
"Fast charging",
"Foldable design",
],
"product_description": "Experience music like never before with our premium wireless headphones.",
"search_terms": "wireless headphones bluetooth noise cancellation",
}
result1 = analyze(test1)
print(json.dumps(result1, ensure_ascii=False, indent=2))
# 测试用例2:关键词分析
print("\n[Test 2] 关键词分析")
test2 = {
"intent": "keyword_analysis",
"product_category": "Electronics > Headphones",
"product_features": ["wireless", "noise cancellation", "bluetooth", "long battery life", "comfortable"],
}
result2 = analyze(test2)
print(json.dumps(result2, ensure_ascii=False, indent=2))
# 测试用例3:竞品对标
print("\n[Test 3] 竞品对标")
test3 = {
"intent": "competitor_benchmark",
"competitor_asin": "B00XXXXXXXX",
"product_title": "Generic Brand Wireless Headphones",
"bullet_points": ["Good sound", "10hr battery", "Lightweight"],
}
result3 = analyze(test3)
print(json.dumps(result3, ensure_ascii=False, indent=2))
# 测试用例4:完整优化
print("\n[Test 4] 完整优化建议包")
test4 = {
"intent": "full_optimization",
"product_title": "Wireless Headphones",
"bullet_points": ["Good sound", "10hr battery"],
"product_description": "Great headphones.",
"search_terms": "headphones",
"product_features": ["wireless", "bluetooth"],
}
result4 = analyze(test4)
print(json.dumps(result4, ensure_ascii=False, indent=2))
# 测试用例5:输入超长检测
print("\n[Test 5] 输入超长校验")
test5 = {"intent": "health_score", "product_title": "x" * 15000}
result5 = analyze(test5)
print(json.dumps(result5, ensure_ascii=False, indent=2))
print("\n" + "=" * 60)
print("自测完成,所有用例执行成功。")
print("=" * 60)
FILE:skill.json
{
"name": "Amazon Listing Analyzer",
"slug": "amazon-listing-analyzer",
"version": "1.0.0",
"description": "Amazon卖家Listing健康度诊断、关键词分析和竞品对标工具,不依赖实时API,输出结构化优化建议包。面向月销售额$5k-$200k的中阶卖家。",
"category": "ecommerce",
"tags": ["amazon", "listing", "keyword-analysis", "competitor-analysis", "ecommerce"],
"author": "Harry (Code Agent)",
"source": "local",
"entry": "handler.py",
"requirements": [],
"runtime": "python3",
"compatibility": {
"openclaw_version": ">=1.0.0"
},
"license": "MIT",
"readme": "SKILL.md"
}
FILE:tests/test_handler.py
#!/usr/bin/env python3
"""
Amazon Listing Analyzer - test_handler.py
至少3个测试用例,验证核心功能正确性。
"""
import unittest
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from handler import (
analyze,
score_title,
score_bullets,
score_description,
score_keywords,
score_compliance,
health_score,
keyword_analysis,
competitor_benchmark,
full_optimization,
)
class TestAmazonListingAnalyzer(unittest.TestCase):
def test_health_score_valid_input(self):
"""测试用例1:健康度评分 - 有效输入"""
data = {
"intent": "health_score",
"product_title": "Premium Wireless Bluetooth Headphones with Noise Cancellation",
"bullet_points": [
"High quality sound",
"30-hour battery life",
"Comfortable fit",
"Fast charging",
"Foldable design",
],
"product_description": "Experience music like never before with our premium headphones.",
"search_terms": "wireless headphones bluetooth noise cancellation",
}
result = analyze(data)
self.assertEqual(result["status"], "success")
self.assertEqual(result["module"], "health_score")
self.assertIn("health_score", result["result"])
hs = result["result"]["health_score"]
self.assertIn("total", hs)
self.assertIn("dimensions", hs)
self.assertIn("summary", hs)
self.assertIsInstance(hs["total"], int)
self.assertGreaterEqual(hs["total"], 0)
self.assertLessEqual(hs["total"], 100)
def test_keyword_analysis(self):
"""测试用例2:关键词分析"""
data = {
"intent": "keyword_analysis",
"product_category": "Electronics > Headphones",
"product_features": ["wireless", "noise cancellation", "bluetooth", "long battery life"],
}
result = analyze(data)
self.assertEqual(result["status"], "success")
self.assertEqual(result["module"], "keyword_analysis")
self.assertIn("keyword_analysis", result["result"])
ka = result["result"]["keyword_analysis"]
self.assertIn("matrix", ka)
self.assertIn("priority_keywords", ka)
self.assertIn("long_tail_keywords", ka)
self.assertIsInstance(ka["matrix"], list)
self.assertIsInstance(ka["priority_keywords"], list)
self.assertIsInstance(ka["long_tail_keywords"], list)
def test_competitor_benchmark(self):
"""测试用例3:竞品对标分析"""
data = {
"intent": "competitor_benchmark",
"competitor_asin": "B00XXXXXXXX",
"product_title": "Generic Brand Wireless Headphones",
"bullet_points": ["Good sound", "10hr battery", "Lightweight", "Sweat resistant", "Foldable"],
}
result = analyze(data)
self.assertEqual(result["status"], "success")
self.assertEqual(result["module"], "competitor_benchmark")
self.assertIn("competitor_benchmark", result["result"])
cb = result["result"]["competitor_benchmark"]
self.assertIn("comparisons", cb)
self.assertIn("gaps", cb)
self.assertIsInstance(cb["comparisons"], list)
self.assertGreater(len(cb["comparisons"]), 0)
def test_full_optimization(self):
"""测试用例4:完整优化建议包"""
data = {
"intent": "full_optimization",
"product_title": "Wireless Headphones",
"bullet_points": ["Good sound", "10hr battery"],
"product_description": "Great headphones for daily use.",
"search_terms": "headphones",
"product_features": ["wireless", "bluetooth"],
}
result = analyze(data)
self.assertEqual(result["status"], "success")
self.assertEqual(result["module"], "full_optimization")
self.assertIn("optimization_package", result["result"])
op = result["result"]["optimization_package"]
self.assertIn("title", op)
self.assertIn("bullets", op)
self.assertIn("description", op)
self.assertIn("keywords", op)
def test_input_too_long(self):
"""测试用例5:输入超长校验"""
data = {
"intent": "health_score",
"product_title": "x" * 15000,
}
result = analyze(data)
self.assertEqual(result["status"], "error")
self.assertIn("errors", result)
self.assertTrue(any("超过10000字符" in e for e in result["errors"]))
def test_score_title_edge_cases(self):
"""测试用例6:标题评分边界情况"""
# 空标题
r = score_title("")
self.assertEqual(r["score"], 0)
self.assertIn("标题为空", r["issues"])
# 正常标题
r = score_title("Sony WH-1000XM5 Wireless Noise Cancelling Headphones")
self.assertGreater(r["score"], 0)
# 标题缺少核心词
r = score_title("Best Product Ever")
self.assertIn("headphone", r["issues"][-1].lower() if r["issues"] else "")
def test_score_bullets_edge_cases(self):
"""测试用例7:五点描述评分边界"""
# 空
r = score_bullets([])
self.assertEqual(r["score"], 0)
# 少于5条
r = score_bullets(["Only one bullet point here"])
self.assertLess(r["score"], 80)
def test_score_compliance_forbidden_words(self):
"""测试用例8:合规检查禁止词"""
r = score_compliance(
"Fake Replica Headphones",
["Best product ever", "Miracle cure for headaches"],
"This product is number 1 top rated",
)
self.assertLess(r["score"], 100)
self.assertTrue(any("fake" in issue.lower() for issue in r["issues"]))
def test_unknown_intent_defaults_to_health_score(self):
"""测试用例9:未知intent默认走健康度评分"""
data = {"intent": "unknown_intent"}
result = analyze(data)
self.assertEqual(result["status"], "success")
self.assertEqual(result["module"], "health_score")
if __name__ == "__main__":
unittest.main(verbosity=2)
Diagnoses Shopify stores on Conversion, SEO, and User Experience, providing actionable tips and curated App recommendations based on best practices and templ...
# Shopify Store Optimizer
## Overview
Shopify Store Optimizer is a descriptive skill that helps Shopify small-to-medium store owners (annual revenue $10k–$500k, moderate technical skill) diagnose store health across three pillars: Conversion Rate, SEO, and User Experience. It delivers actionable optimization suggestions and curated App recommendations — all based on built-in templates, checklists, and best-practice libraries — without requiring real-time API connectivity.
## Trigger
**Primary trigger:** User asks for a Shopify store health check, optimization audit, or improvement recommendations.
**Example prompts:**
- "帮我诊断一下我的Shopify店铺"
- "Shopify店铺健康度检查"
- "How can I improve my Shopify store conversion rate?"
- "我的店SEO做得怎么样?"
- "推荐一些Shopify店铺优化App"
**Activation conditions:** None — this skill is purely descriptive and always available.
## Workflow
```
1. Receive user input (store description, URL, or specific concern)
2. Identify which diagnostic areas are relevant (Conversion / SEO / UX / All)
3. Apply built-in templates and best-practice libraries
4. Generate a structured diagnostic report with:
- Health scores per area (Low / Medium / High)
- Specific findings and issues
- Prioritized optimization suggestions
- Curated App recommendations
5. Return the report in clear, actionable markdown
```
**Diagnostic Areas:**
| Area | What it covers |
|------|----------------|
| **Conversion Rate** | Product pages, checkout flow, trust signals, pricing psychology, CTA clarity |
| **SEO** | Title/meta, image alt text, URL structure, blog/content strategy, core web vitals guidance |
| **User Experience** | Navigation, mobile-friendliness, page speed, accessibility, search usability |
## I/O
**Input (user provides one or more):**
- Store URL or description
- Specific concern (e.g., "low conversion", "low traffic")
- Annual revenue range (optional, for context)
- Number of products (optional)
**Output:**
A structured markdown report containing:
- Overall health summary
- Per-area health scores and findings
- Prioritized action items
- App recommendations with rationale
- Best-practice tips specific to the concern
## Safety
- **No real API calls:** This skill does not connect to Shopify APIs, Google Analytics, or any external service.
- **No PII collected:** The skill processes only generic store descriptors provided voluntarily by the user.
- **No financial advice:** Recommendations are informational only; actual business decisions remain with the store owner.
- **No installation required:** All content is built-in templates and libraries.
## Examples
### Example 1: Full Diagnostic
**Input:** "我的Shopify店有50个产品,月访问量3000,但转化率只有0.8%,帮我看看哪里有问题"
**Output:**
```
## 🏪 Shopify 店铺健康度报告
### 整体健康度:⚠️ 中等偏低
---
### 1. 转化率分析 — 得分:🟡 中等
**现状:**
- 转化率 0.8%,行业平均 1.4%–2.5%,偏低
- 访问量 3000/月,预期订单 24–75,实际约 24
**发现:**
- 产品页缺少社会证明(评价/销量)
- CTA 按钮不够突出
- 缺少信任标识(安全徽章、退款政策)
**优化建议(优先级排序):**
1. 添加产品评价插件(如 Shopify Reviews / Judge.me)
2. 在产品页显著位置展示库存数量或"热销"标签
3. 在 Checkout 页面添加安全支付标识
4. A/B 测试不同的 "Add to Cart" 按钮颜色和文案
**推荐 App:**
- Judge.me Product Reviews(免费起步,$9/月)
- Loox Reviews & Photos($9.9/月)
- Ali Reviews(免费起步)
---
### 2. SEO 分析 — 得分:🟡 中等
**现状:**
- 缺少 SEO 审计数据,建议配合 Google Search Console
**发现:**
- 产品标题可能缺少目标关键词
- 图片缺少 Alt 文本
- URL 结构未做关键词优化
**优化建议:**
1. 为每个产品编写独特、关键词丰富的 Meta Title(50–60字符)
2. 为所有产品图片添加描述性 Alt 文本
3. 在 Shopify BLOG 发布相关产品内容文章
4. 提交 sitemap.xml 到 Google Search Console
**推荐 App:**
- Plug in SEO(免费/付费,$9/月)
- SEO Manager($9/月)
- Smart SEO(免费起步)
---
### 3. 用户体验分析 — 得分:🟡 中等
**发现:**
- 移动端体验未检测(建议用 Google PageSpeed Insights 自测)
- 导航层级可能过深
- 缺少站点搜索功能
**优化建议:**
1. 确保主题移动端响应式测试通过
2. 简化菜单结构,重要页面 2–3 次点击可达
3. 启用 Shopify 内置搜索并优化搜索结果排序
**推荐 App:**
- Boost Commerce Search & Filter($9.9/月)
- Raja Theme — Quick View(免费)
---
### 📋 下一步行动清单
| 优先级 | 行动 | 预计时间 |
|--------|------|---------|
| 🔴 高 | 添加产品评价系统 | 1–2 小时 |
| 🔴 高 | 优化产品页 Meta 信息 | 3–4 小时 |
| 🟡 中 | 添加信任标识到结算页 | 30 分钟 |
| 🟡 中 | 启用 SEO App 扫描 | 20 分钟 |
| 🟢 低 | 优化导航和搜索 | 1–2 小时 |
---
*本报告基于 Shopify 最佳实践库生成,不涉及实时 API 调用。建议定期复检。*
```
### Example 2: SEO-Only Query
**Input:** "我的Shopify店SEO做得怎么样?"
**Output:** SEO-focused section only (same structure as above).
### Example 3: App Recommendation Only
**Input:** "推荐一些能提高转化率的App"
**Output:** Conversion Rate App recommendations with pricing and rationale.
## Acceptance Criteria
1. **Skill activates** on any of the trigger phrases listed above.
2. **handler.py runs standalone:** `python3 handler.py` produces valid markdown output without external dependencies.
3. **No network calls:** All content comes from built-in templates; runs fully offline.
4. **Report is actionable:** Output includes specific findings, prioritized suggestions, and App recommendations.
5. **Tests pass:** `python3 tests/test_handler.py` runs ≥ 3 test cases and all pass.
6. **Metadata complete:** `skill.json` and `.claw/identity.json` are present and valid.
7. **Language flexibility:** Handles both Chinese and English inputs.
FILE:handler.py
#!/usr/bin/env python3
"""
Shopify Store Optimizer - handler.py
Generates store health diagnostic reports based on built-in templates and best-practice libraries.
No real-time API calls are made.
"""
import json
import re
import sys
from typing import Literal
# ─────────────────────────────────────────────
# BUILT-IN TEMPLATES & BEST-PRACTICE LIBRARIES
# ─────────────────────────────────────────────
CONVERSION_TIPS = [
{
"issue": "产品页缺少社会证明",
"suggestion": "添加产品评价插件(Judge.me / Loox),在产品页展示星级评分和真实买家评价",
"priority": "high",
"effort_hours": 1,
"apps": [
{"name": "Judge.me Product Reviews", "price": "免费起步 / $9/月", "url": "https://judge.me/"},
{"name": "Loox Reviews & Photos", "price": "$9.9/月", "url": "https://loox.io/"},
{"name": "Ali Reviews", "price": "免费起步", "url": "https://alireviews.com/"},
],
},
{
"issue": "CTA 按钮不够突出",
"suggestion": "将 'Add to Cart' 按钮改为对比色(与背景形成高对比),增大尺寸,添加微动效",
"priority": "high",
"effort_hours": 0.5,
"apps": [],
},
{
"issue": "缺少信任标识",
"suggestion": "在产品页和结算页添加安全支付徽章(SSL / Visa / Mastercard)、退款政策提示",
"priority": "high",
"effort_hours": 0.5,
"apps": [
{"name": "H Trust Badge", "price": "免费 / $5/月", "url": "https://h-trust-badge.com/"},
],
},
{
"issue": "未展示库存/热销状态",
"suggestion": "在产品卡片添加'仅剩 X 件'或'热销中'标签,制造紧迫感",
"priority": "medium",
"effort_hours": 0.5,
"apps": [],
},
{
"issue": "缺少 Urgency / FOMO 元素",
"suggestion": "添加倒计时插件或'X 人正在查看此商品'浮动通知",
"priority": "medium",
"effort_hours": 1,
"apps": [
{"name": "Sales Pop Master", "price": "免费 / $9.9/月", "url": "https://sale-pop-master.com/"},
{"name": "UFE: Urgency Fomo", "price": "$9.9/月", "url": "https://urgency-fomo.com/"},
],
},
{
"issue": "结算流程过长",
"suggestion": "启用 Shopify Checkout 优化,启用 Guest Checkout,减少必填字段",
"priority": "high",
"effort_hours": 1,
"apps": [
{"name": "Shopify Checkout Plus", "price": "$9/月", "url": "https://checkout-plus.com/"},
],
},
{
"issue": "缺少 A/B 测试",
"suggestion": "引入 Google Optimize 或 Shopify 内置的渠道测试数据,优化产品页文案和布局",
"priority": "medium",
"effort_hours": 2,
"apps": [],
},
]
SEO_TIPS = [
{
"issue": "产品 Meta Title / Description 缺失或重复",
"suggestion": "为每个产品编写独特、含目标关键词的 Meta Title(50–60字符)和 Description(150–160字符)",
"priority": "high",
"effort_hours": 3,
"apps": [
{"name": "Plug in SEO", "price": "免费 / $9/月", "url": "https://plugineseo.com/"},
{"name": "SEO Manager", "price": "$9/月", "url": "https://seomanager.app/"},
{"name": "Smart SEO", "price": "免费起步", "url": "https://smartseo.app/"},
],
},
{
"issue": "图片缺少 Alt 文本",
"suggestion": "为所有产品图片添加描述性 Alt 文本,包含产品名和目标关键词",
"priority": "high",
"effort_hours": 2,
"apps": [
{"name": "AltText.ai", "price": "$9/月起", "url": "https://alttext.ai/"},
],
},
{
"issue": "URL 结构未做关键词优化",
"suggestion": "确保产品 URL 包含简短的产品名(而非随机 ID),例如 /products/organic-coffee-beans",
"priority": "medium",
"effort_hours": 1,
"apps": [],
},
{
"issue": "缺少 BLOG / 内容营销",
"suggestion": "每周发布 1–2 篇与产品相关的博客文章,布局长尾关键词,提升自然流量",
"priority": "medium",
"effort_hours": 4,
"apps": [
{"name": "Shopify Blog+", "price": "免费", "url": "https://shopify.com/blog"},
],
},
{
"issue": "未提交 sitemap.xml",
"suggestion": "在 Shopify 后台 → 设置 → 搜索引擎主动提交 sitemap.xml 到 Google Search Console",
"priority": "medium",
"effort_hours": 0.5,
"apps": [],
},
{
"issue": "缺少结构化数据(Schema Markup)",
"suggestion": "为产品页添加 Product Schema,包含价格、评分、库存状态,帮助搜索引擎理解页面内容",
"priority": "medium",
"effort_hours": 2,
"apps": [
{"name": "Schema Plus", "price": "$5/月", "url": "https://schema-plus.com/"},
],
},
{
"issue": "页面加载速度慢",
"suggestion": "压缩图片(WebP 格式)、启用_lazy load、精简主题代码、使用 CDN",
"priority": "high",
"effort_hours": 2,
"apps": [
{"name": "TinyIMG", "price": "免费 / $10/月", "url": "https://tinyimg.io/"},
{"name": "Jetpack", "price": "免费 / $9/月", "url": "https://jetpack.com/"},
],
},
]
UX_TIPS = [
{
"issue": "导航层级过深",
"suggestion": "将重要页面控制在 2–3 次点击内可达;使用 Mega Menu 展示热门分类",
"priority": "high",
"effort_hours": 1,
"apps": [
{"name": "EA Store Sales", "price": "免费", "url": "https://ea-storesales.com/"},
],
},
{
"issue": "缺少站点搜索功能",
"suggestion": "启用 Shopify 内置搜索,并安装 Boost Commerce 优化搜索排序和过滤体验",
"priority": "medium",
"effort_hours": 1,
"apps": [
{"name": "Boost Commerce Search & Filter", "price": "$9.9/月", "url": "https://boostcommerce.net/"},
{"name": "Searchanise", "price": "免费 / $9.9/月", "url": "https://searchanise.com/"},
],
},
{
"issue": "移动端体验未优化",
"suggestion": "使用 Google PageSpeed Insights 测试移动端,根据建议调整图片大小和主题布局",
"priority": "high",
"effort_hours": 2,
"apps": [],
},
{
"issue": "结账页体验差",
"suggestion": "启用 Express Checkout(Shop Pay / Apple Pay / Google Pay),减少用户输入成本",
"priority": "high",
"effort_hours": 1,
"apps": [
{"name": "Shop Pay", "price": "免费", "url": "https://shopify.com/checkout"},
],
},
{
"issue": "页面布局杂乱",
"suggestion": "遵循 F 形阅读模式,重要信息(左上→右下)优先展示;留白充足,减少视觉噪音",
"priority": "medium",
"effort_hours": 2,
"apps": [],
},
{
"issue": "缺少 404 页面处理",
"suggestion": "自定义 404 页面,引导用户返回首页或搜索,避免直接跳出",
"priority": "low",
"effort_hours": 0.5,
"apps": [],
},
]
APP_CATALOG = {
"conversion": [
{"name": "Judge.me Product Reviews", "price": "免费起步 / $9/月", "rationale": "安装量最大的评价插件,支持照片评价和邮件催评"},
{"name": "Loox Reviews & Photos", "price": "$9.9/月", "rationale": "支持图片评价,自动收集社交媒体图片,有免费试用"},
{"name": "Ali Reviews", "price": "免费起步", "rationale": "支持速卖通评价导入,免费计划功能完整"},
{"name": "Sales Pop Master", "price": "免费 / $9.9/月", "rationale": "展示实时销售弹窗,制造紧迫感和社会证明"},
{"name": "UFE: Urgency Fomo", "price": "$9.9/月", "rationale": "倒计时、库存紧迫感、多合一 urgency 工具"},
{"name": "H Trust Badge", "price": "免费 / $5/月", "rationale": "在结算页展示支付安全徽章,提升信任度"},
{"name": "Shop Pay", "price": "免费", "rationale": "Shopify 官方快速支付,转化率提升平均 1.7 倍"},
],
"seo": [
{"name": "Plug in SEO", "price": "免费 / $9/月", "rationale": "自动检测 100+ SEO 问题,覆盖元标签、死链、结构化数据"},
{"name": "SEO Manager", "price": "$9/月", "rationale": "批量编辑 Meta 信息,支持 JSON-LD Schema,功能全面"},
{"name": "Smart SEO", "price": "免费起步", "rationale": "自动生成 Meta 标签和 Alt 文本,适合多产品店铺"},
{"name": "AltText.ai", "price": "$9/月起", "rationale": "AI 自动为图片生成 Alt 文本,大幅节省人工时间"},
{"name": "TinyIMG", "price": "免费 / $10/月", "rationale": "自动压缩 Shopify 图片(WebP),提升页面加载速度"},
{"name": "Schema Plus", "price": "$5/月", "rationale": "一键添加 Product / Review Schema,提升搜索展示效果"},
],
"ux": [
{"name": "Boost Commerce Search & Filter", "price": "$9.9/月", "rationale": "强大的搜索体验优化,支持过滤、同义词、拼写纠错"},
{"name": "Searchanise", "price": "免费 / $9.9/月", "rationale": "即时搜索,支持多语言和个性化排序"},
{"name": "Raja Theme Quick View", "price": "免费", "rationale": "无需打开产品页即可预览,适合产品多的店铺"},
{"name": "EA Store Sales", "price": "免费", "rationale": "轻量 Mega Menu,优化大型店铺导航"},
],
}
CHECKLIST_CONVERSION = [
"☐ 已安装产品评价插件并在产品页展示评分",
"☐ 'Add to Cart' 按钮使用高对比色,尺寸≥ 44px",
"☐ 信任标识(安全支付徽章)已展示在产品页和结算页",
"☐ 已启用 Guest Checkout,减少结算步骤",
"☐ 产品页展示库存数量或'热销'标签",
"☐ 已启用至少一种快速支付(Shop Pay / Apple Pay)",
"☐ 结算页说明退款政策(可放在 footer 或结算页侧栏)",
]
CHECKLIST_SEO = [
"☐ 所有产品有独特的 Meta Title(50–60字符)",
"☐ 所有产品有独特的 Meta Description(150–160字符)",
"☐ 所有产品图片有描述性 Alt 文本",
"☐ 产品 URL 包含可读产品名(不含随机 ID)",
"☐ 已提交 sitemap.xml 到 Google Search Console",
"☐ 页面加载速度(LCP)经 Google PageSpeed Insights 验证 < 2.5s",
"☐ 已发布至少 5 篇与产品相关的博客文章",
"☐ 已添加 Product Schema 结构化数据",
]
CHECKLIST_UX = [
"☐ Google Mobile-Friendly Test 通过",
"☐ 重要页面 3 次点击内可达",
"☐ 启用 Shopify 内置搜索并测试结果相关性",
"☐ 结算流程≤ 3 步(地址→运输→支付)",
"☐ 自定义 404 页面已配置",
"☐ 页面留白充足,无过度拥挤",
"☐ 字体大小≥ 16px,正文可读性良好",
]
SCORE_THRESHOLDS = {
"conversion": {"low": 0.5, "medium": 1.0, "high": 2.0}, # industry avg 1.4%-2.5%
"seo": {"low": 0, "medium": 60, "high": 80}, # score 0-100
"ux": {"low": 0, "medium": 60, "high": 80},
}
# ─────────────────────────────────────────────
# CORE DIAGNOSTIC ENGINE
# ─────────────────────────────────────────────
class DiagnosticEngine:
def __init__(self, user_input: str):
self.raw_input = user_input
self.area = self._detect_area()
self.conversion_rate = self._extract_conversion_rate()
self.products_count = self._extract_products_count()
self.traffic = self._extract_traffic()
def _detect_area(self) -> str:
text = self.raw_input.lower()
if any(k in text for k in ["seo", "搜索", "meta", "关键词", "google", "排名", "流量", "网站速度"]):
return "seo"
if any(k in text for k in ["转化", "conversion", "订单", "销售", "购买", "cart", "checkout", "支付"]):
return "conversion"
if any(k in text for k in ["ux", "体验", "用户", "导航", "页面", "速度", "mobile", "移动"]):
return "ux"
return "all"
def _extract_conversion_rate(self) -> float | None:
m = re.search(r"转化[率\s]*[::]?\s*([0-9.]+)\s*%", self.raw_input)
if m:
return float(m.group(1))
m = re.search(r"conversion\s*rate\s*[:\-]?\s*([0-9.]+)\s*%", self.raw_input, re.I)
if m:
return float(m.group(1))
m = re.search(r"([0-9.]+)\s*%", self.raw_input)
if m and len(self.raw_input) < 200:
return float(m.group(1))
return None
def _extract_products_count(self) -> int | None:
m = re.search(r"(\d+)\s*个?\s*产品", self.raw_input)
if m:
return int(m.group(1))
m = re.search(r"(\d+)\s*products?", self.raw_input, re.I)
if m:
return int(m.group(1))
return None
def _extract_traffic(self) -> int | None:
m = re.search(r"月\s*访问[量是]*[::]?\s*([0-9,]+)", self.raw_input)
if m:
return int(m.group(1).replace(",", ""))
m = re.search(r"月\s*流量[::]?\s*([0-9,]+)", self.raw_input)
if m:
return int(m.group(1).replace(",", ""))
m = re.search(r"([0-9,]+)\s*(?:monthly|month)\s*(?:visits?|traffic)", self.raw_input, re.I)
if m:
return int(m.group(1).replace(",", ""))
return None
def _score_conversion(self) -> tuple[str, str]:
cr = self.conversion_rate
if cr is None:
return "unknown", "🟡 中等(数据不足)"
if cr < 1.0:
return "low", "🔴 偏低"
if cr < 1.5:
return "medium", "🟡 中等"
return "high", "🟢 良好"
def _score_seo(self) -> tuple[str, str]:
# No real API, return medium by default with advisory note
return "medium", "🟡 中等(建议配合 Google Search Console 确认)"
def _score_ux(self) -> tuple[str, str]:
return "medium", "🟡 中等(建议用 Google PageSpeed Insights 自测)"
def generate_report(self) -> str:
area = self.area
lines = [
"## 🏪 Shopify 店铺健康度报告",
"",
"### 📊 整体健康度",
]
if area == "all":
cr_score, cr_label = self._score_conversion()
seo_score, seo_label = self._score_seo()
ux_score, ux_label = self._score_ux()
# Overall
scores = [cr_score, seo_score, ux_score]
if "unknown" in scores:
scores = [s for s in scores if s != "unknown"]
if not scores:
overall = "🟡 中等(信息不足,请提供更多店铺数据以获得精准诊断)"
elif scores.count("low") >= 2:
overall = "⚠️ 中等偏低(多个维度存在优化空间)"
elif scores.count("high") >= 2:
overall = "✅ 良好(店铺整体表现健康)"
else:
overall = "⚠️ 中等(有改善空间)"
lines.append(overall)
lines.append("")
lines.append("| 诊断维度 | 健康评分 |")
lines.append("|----------|----------|")
lines.append(f"| 转化率 | {cr_label} |")
lines.append(f"| SEO | {seo_label} |")
lines.append(f"| 用户体验 | {ux_label} |")
elif area == "conversion":
cr_score, cr_label = self._score_conversion()
lines.append(cr_label)
if self.conversion_rate:
lines.append(f"(当前值:{self.conversion_rate}%,行业均值 1.4%–2.5%)")
elif area == "seo":
seo_score, seo_label = self._score_seo()
lines.append(seo_label)
elif area == "ux":
ux_score, ux_label = self._score_ux()
lines.append(ux_label)
lines.append("")
lines.append("---")
lines.append("")
# ── Conversion Section ──
if area in ("all", "conversion"):
cr_score, cr_label = self._score_conversion()
lines.extend([
"### 1️⃣ 转化率分析 — 得分:" + cr_label,
"",
])
if self.conversion_rate and self.traffic:
expected_low = self.traffic * 0.014
expected_high = self.traffic * 0.025
lines.extend([
f"**数据概览:**",
f"- 当前转化率:{self.conversion_rate}%",
f"- 月访问量:{self.traffic:,}",
f"- 预期月订单(行业均值):{expected_low:.0f}–{expected_high:.0f}",
f"- 实际月订单(估算):约 {self.traffic * self.conversion_rate / 100:.0f}",
"",
])
elif self.conversion_rate:
lines.append(f"**当前转化率:{self.conversion_rate}%**(行业均值 1.4%–2.5%)")
lines.extend(["**转化率最佳实践检查清单:**"])
for item in CHECKLIST_CONVERSION:
lines.append(item)
lines.append("")
if cr_score in ("low", "medium", "unknown"):
lines.append("**重点优化建议:**")
for tip in CONVERSION_TIPS[:4]:
lines.append(f"- 🔸 **{tip['issue']}**:{tip['suggestion']}")
if tip["apps"]:
for app in tip["apps"][:2]:
lines.append(f" - {app['name']}({app['price']})")
lines.append("")
# ── SEO Section ──
if area in ("all", "seo"):
lines.extend([
"### 2️⃣ SEO 分析 — 得分:🟡 中等",
"",
"*提示:无实时 API 访问,建议配合 Google Search Console 和 Google PageSpeed Insights 获取真实数据。*",
"",
"**SEO 最佳实践检查清单:**",
])
for item in CHECKLIST_SEO:
lines.append(item)
lines.append("")
lines.append("**重点优化建议:**")
for tip in SEO_TIPS[:4]:
lines.append(f"- 🔸 **{tip['issue']}**:{tip['suggestion']}")
if tip["apps"]:
for app in tip["apps"][:2]:
lines.append(f" - {app['name']}({app['price']})")
lines.append("")
# ── UX Section ──
if area in ("all", "ux"):
lines.extend([
"### 3️⃣ 用户体验分析 — 得分:🟡 中等",
"",
"*提示:建议使用 Google PageSpeed Insights(pagespeed.web.dev)实测移动端体验。*",
"",
"**用户体验最佳实践检查清单:**",
])
for item in CHECKLIST_UX:
lines.append(item)
lines.append("")
lines.append("**重点优化建议:**")
for tip in UX_TIPS[:4]:
lines.append(f"- 🔸 **{tip['issue']}**:{tip['suggestion']}")
if tip["apps"]:
for app in tip["apps"][:2]:
lines.append(f" - {app['name']}({app['price']})")
lines.append("")
# ── App Recommendations ──
lines.extend([
"### 🌐 推荐 App 汇总",
"",
"**转化率提升:**",
])
for app in APP_CATALOG["conversion"][:4]:
lines.append(f"- **{app['name']}** — {app['price']} \n {app['rationale']}")
lines.append("")
lines.append("**SEO 优化:**")
for app in APP_CATALOG["seo"][:4]:
lines.append(f"- **{app['name']}** — {app['price']} \n {app['rationale']}")
lines.append("")
lines.append("**用户体验:**")
for app in APP_CATALOG["ux"][:3]:
lines.append(f"- **{app['name']}** — {app['price']} \n {app['rationale']}")
lines.append("")
# ── Priority Action Items ──
if area == "all":
lines.extend([
"### 📋 下一步行动清单(优先级排序)",
"",
"| 优先级 | 行动 | 预计时间 |",
"|--------|------|---------|",
"| 🔴 高 | 安装评价插件(Judge.me) | 1–2 小时 |",
"| 🔴 高 | 优化产品页 Meta 信息 | 3–4 小时 |",
"| 🟡 中 | 添加信任标识到结算页 | 30 分钟 |",
"| 🟡 中 | 启用 SEO App 扫描问题 | 20 分钟 |",
"| 🟢 低 | 优化导航和站点搜索 | 1–2 小时 |",
"",
])
lines.extend([
"---",
"",
"*本报告基于 Shopify 最佳实践库生成,不涉及实时 API 调用。建议定期复检并结合 Google Analytics / Search Console 数据做最终判断。*",
])
return "\n".join(lines)
# ─────────────────────────────────────────────
# HANDLER ENTRY POINT
# ─────────────────────────────────────────────
def handle(user_input: str) -> str:
"""
Main handler. Takes a user query string and returns a markdown report.
"""
engine = DiagnosticEngine(user_input)
return engine.generate_report()
if __name__ == "__main__":
# Self-test / demo mode
print("=" * 60)
print("Shopify Store Optimizer - Self Test")
print("=" * 60)
print()
test_cases = [
# Test 1: Full diagnostic
"我的Shopify店有50个产品,月访问量3000,但转化率只有0.8%,帮我看看哪里有问题",
# Test 2: SEO only
"我的Shopify店SEO做得怎么样?",
# Test 3: App recommendation
"推荐一些能提高转化率的App",
# Test 4: English input
"My Shopify store has low conversion rate, what should I do?",
]
for i, tc in enumerate(test_cases, 1):
print(f"\n{'─' * 60}")
print(f"[Test Case {i}]")
print(f"Input: {tc}")
print()
result = handle(tc)
print(result)
print()
print("=" * 60)
print("All self-test cases completed.")
print("=" * 60)
FILE:skill.json
{
"name": "Shopify Store Optimizer",
"slug": "shopify-store-optimizer",
"version": "1.0.0",
"description": "诊断 Shopify 店铺健康度(转化率、SEO、用户体验),提供优化建议和 App 推荐。基于内置模板和最佳实践库,无需实时 API 连接。",
"author": "Harry (OpenClaw)",
"tags": ["shopify", "ecommerce", "conversion-rate", "seo", "ux", "store-health", "diagnosis"],
"trigger_keywords": [
"shopify",
"店铺诊断",
"店铺健康",
"转化率",
"seo",
"用户体验",
"conversion rate",
"shopify store",
"shopify optimization",
"shopify app 推荐"
],
"language": "zh-CN",
"outputs": "markdown",
"requires_api": false,
"readiness": "stable"
}
FILE:tests/test_handler.py
"""
Tests for Shopify Store Optimizer handler.py
Run: python3 tests/test_handler.py
"""
import sys
import os
# Ensure handler is importable
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from handler import DiagnosticEngine, handle, APP_CATALOG, CONVERSION_TIPS, SEO_TIPS, UX_TIPS
def test_area_detection_conversion():
"""Test that conversion-related input is correctly detected."""
engine = DiagnosticEngine("我的Shopify店转化率只有0.5%,怎么处理")
assert engine.area == "conversion", f"Expected 'conversion', got '{engine.area}'"
print("✓ test_area_detection_conversion passed")
def test_area_detection_seo():
"""Test that SEO-related input is correctly detected."""
engine = DiagnosticEngine("我的店SEO做得怎么样?")
assert engine.area == "seo", f"Expected 'seo', got '{engine.area}'"
print("✓ test_area_detection_seo passed")
def test_area_detection_all():
"""Test that general health query triggers 'all' area."""
engine = DiagnosticEngine("帮我看看我的Shopify店有哪些问题")
assert engine.area == "all", f"Expected 'all', got '{engine.area}'"
print("✓ test_area_detection_all passed")
def test_area_detection_ux():
"""Test that UX-related input is correctly detected."""
engine = DiagnosticEngine("我的店移动端体验很差,怎么优化")
assert engine.area == "ux", f"Expected 'ux', got '{engine.area}'"
print("✓ test_area_detection_ux passed")
def test_conversion_rate_extraction():
"""Test numeric conversion rate extraction from Chinese text."""
engine = DiagnosticEngine("我的店转化率是1.8%,转化很低")
assert engine.conversion_rate == 1.8, f"Expected 1.8, got {engine.conversion_rate}"
print("✓ test_conversion_rate_extraction passed")
def test_products_count_extraction():
"""Test product count extraction."""
engine = DiagnosticEngine("我有200个产品,销量不好")
assert engine.products_count == 200, f"Expected 200, got {engine.products_count}"
print("✓ test_products_count_extraction passed")
def test_traffic_extraction():
"""Test monthly traffic extraction."""
engine = DiagnosticEngine("月访问量是5000,转化很差")
assert engine.traffic == 5000, f"Expected 5000, got {engine.traffic}"
print("✓ test_traffic_extraction passed")
def test_handle_returns_string():
"""Test that handle() returns a non-empty string."""
result = handle("帮我诊断Shopify店铺")
assert isinstance(result, str), "Result must be a string"
assert len(result) > 100, "Result too short to be a valid report"
assert "Shopify" in result, "Result must contain 'Shopify'"
print("✓ test_handle_returns_string passed")
def test_report_contains_sections():
"""Test that the generated report contains expected sections."""
result = handle("我的Shopify店月访问量3000,转化率0.8%")
assert "##" in result, "Report must have markdown headers"
assert "转化率" in result, "Report must mention conversion rate"
assert "SEO" in result or "seo" in result.lower(), "Report must mention SEO"
print("✓ test_report_contains_sections passed")
def test_conversion_low_score():
"""Test that conversion rate < 1% is scored as low."""
engine = DiagnosticEngine("转化率0.5%")
score, label = engine._score_conversion()
assert score == "low", f"Expected 'low', got '{score}'"
print("✓ test_conversion_low_score passed")
def test_conversion_healthy_score():
"""Test that conversion rate >= 1.5% is scored as good."""
engine = DiagnosticEngine("转化率2.5%")
score, label = engine._score_conversion()
assert score == "high", f"Expected 'high', got '{score}'"
print("✓ test_conversion_healthy_score passed")
def test_app_catalog_has_entries():
"""Test that APP_CATALOG has entries for all three categories."""
assert "conversion" in APP_CATALOG, "APP_CATALOG must have 'conversion'"
assert "seo" in APP_CATALOG, "APP_CATALOG must have 'seo'"
assert "ux" in APP_CATALOG, "APP_CATALOG must have 'ux'"
assert len(APP_CATALOG["conversion"]) > 0, "conversion apps list must not be empty"
print("✓ test_app_catalog_has_entries passed")
def test_tips_are_nonempty():
"""Test that all tip libraries are non-empty."""
assert len(CONVERSION_TIPS) > 0, "CONVERSION_TIPS must not be empty"
assert len(SEO_TIPS) > 0, "SEO_TIPS must not be empty"
assert len(UX_TIPS) > 0, "UX_TIPS must not be empty"
print("✓ test_tips_are_nonempty passed")
def test_report_no_api_claim():
"""Test that the report explicitly states no real API is called."""
result = handle("帮我诊断店铺")
assert "API" in result or "api" in result.lower(), "Report must mention no-API nature"
print("✓ test_report_no_api_claim passed")
def test_english_input_handled():
"""Test that English input is handled gracefully."""
engine = DiagnosticEngine("My Shopify store conversion rate is 0.9%")
assert engine.area == "conversion", f"Expected 'conversion' for English input, got '{engine.area}'"
result = handle("How can I improve my Shopify store conversion rate?")
assert len(result) > 50, "English input should produce a valid report"
print("✓ test_english_input_handled passed")
if __name__ == "__main__":
print("=" * 60)
print("Shopify Store Optimizer - Test Suite")
print("=" * 60)
print()
tests = [
test_area_detection_conversion,
test_area_detection_seo,
test_area_detection_all,
test_area_detection_ux,
test_conversion_rate_extraction,
test_products_count_extraction,
test_traffic_extraction,
test_handle_returns_string,
test_report_contains_sections,
test_conversion_low_score,
test_conversion_healthy_score,
test_app_catalog_has_entries,
test_tips_are_nonempty,
test_report_no_api_claim,
test_english_input_handled,
]
passed = 0
failed = 0
for test in tests:
try:
test()
passed += 1
except AssertionError as e:
print(f"✗ {test.__name__} FAILED: {e}")
failed += 1
except Exception as e:
print(f"✗ {test.__name__} ERROR: {e}")
failed += 1
print()
print("=" * 60)
print(f"Results: {passed} passed, {failed} failed")
print("=" * 60)
if failed > 0:
sys.exit(1)
else:
print("All tests passed!")
sys.exit(0)
Analyze daily decisions with pros and cons, alternatives, and provide structured tools and processes for clearer choices.
# Daily Decision Helper(日常决策助手)
## Overview
帮助用户结构化分析日常决策,提供利弊框架和决策工具。
## Trigger
- 要不要...
- 纠结
- 怎么选
- 买还是不买
- 决策
## Output
JSON: {decisionType, prosConsFramework{pros[], cons[], alternatives[], weighted_factors[]}, decisionTools{}, recommendedProcess[]}
FILE:handler.py
"""Daily Decision Helper - 日常决策助手"""
import json
import re
from typing import List
def parse_decision(text: str) -> dict:
return {"question": text.strip(), "type": None}
def categorize_decision(text: str) -> str:
t = text.lower()
if any(k in t for k in ["买", "购物", "选哪个", "性价比"]): return "消费选择"
if any(k in t for k in ["工作", "跳槽", "辞职", "offer"]): return "职业发展"
if any(k in t for k in ["孩子", "上学", "培训班", "教育"]): return "教育决策"
if any(k in t for k in ["搬家", "买房", "换房", "定居"]): return "居住决策"
return "一般决策"
def pros_cons_list(question: str) -> dict:
return {
"question": question,
"pros": ["请列出你认为选择A的主要好处(至少3条)"],
"cons": ["请列出你认为选择A的主要代价/风险(至少3条)"],
"alternatives": ["还有没有第3种选择?"],
"weighted_factors": [
{"factor": "经济成本", "weight": "高/中/低"},
{"factor": "时间成本", "weight": "高/中/低"},
{"factor": "情绪影响", "weight": "高/中/低"},
{"factor": "长期影响", "weight": "高/中/低"}
]
}
def six_months_test(question: str) -> str:
return f"问自己:6个月后回头看,这个决定还重要吗?如果不重要,说明现在过度担忧了。如果重要,6个月后你会后悔吗?"
def handle(text: str) -> dict:
parsed = parse_decision(text)
dtype = categorize_decision(text)
pc = pros_cons_list(parsed["question"])
return {
"decisionType": dtype,
"question": parsed["question"],
"prosConsFramework": pc,
"decisionTools": {
"sixMonthsTest": six_months_test(parsed["question"]),
"worseFirst": "先想象最坏结果,然后问自己:我能接受吗?能接受就去做,不能接受就调整方案。",
"reversed": "想象你选了B(反向选项),6个月后你会怎么想?"
},
"recommendedProcess": [
"第一步:清晰描述你要做的决定(不是你的担忧)",
"第二步:用利弊清单结构化分析(让ChatGPT帮你写)",
"第三步:做加法(列出所有好处)+ 做减法(列出所有风险)",
"第四步:问6个月后测试",
"第五步:做出选择,然后全力执行"
]
}
if __name__ == "__main__":
for tc in ["要不要给孩子报那个思维课,5800一学期", "现在工作还可以,但有个新机会要去面试,跳槽"]:
r = handle(tc)
print(f"Input: {tc}\n -> 类型: {r['decisionType']}\n -> 第一步: {r['recommendedProcess'][0]}\n -> 6个月测试: {r['decisionTools']['sixMonthsTest'][:30]}...\n")
FILE:skill.json
{
"name": "daily-decision-helper",
"slug": "daily-decision-helper",
"version": "0.1.0",
"description": "auto-generated skeleton",
"author": "Harry",
"tags": ["lifestyle"],
"handler": "handler.py"
}
Recommend plants and garden layouts based on your space, experience, and goals, with seasonal tips and common mistakes to avoid.
# Home Garden Planner(家庭花园规划师)
## Overview
根据用户的种植空间、经验水平和目的,推荐适合的植物品种,提供种植规划和常见错误提示。
## Trigger
- 种花
- 阳台种菜
- 养植物
- 花园规划
## Output
JSON: {spaceType, gardenPlan{recommendedPlants[], layout_suggestion, seasonal_tips}, tools[], commonMistakes[]}
FILE:handler.py
"""Home Garden Planner - 家庭花园规划师"""
import json
import re
PLANTS = {
"入门推荐": [
{"name": "绿萝", "difficulty": "★☆☆☆☆", "light": "耐阴", "water": "土干浇水", "suitable": "新手/室内"},
{"name": "吊兰", "difficulty": "★☆☆☆☆", "light": "散射光", "water": "保持湿润", "suitable": "新手/室内"},
{"name": "薄荷", "difficulty": "★★☆☆☆", "light": "充足光照", "water": "每天浇水", "suitable": "阳台/可食用"},
],
"阳台推荐": [
{"name": "月季", "difficulty": "★★★☆☆", "light": "全日照", "water": "见干见湿", "suitable": "有阳台/观花"},
{"name": "茉莉", "difficulty": "★★★☆☆", "light": "充足光照", "water": "保持湿润", "suitable": "阳台/芳香"},
{"name": "小番茄", "difficulty": "★★☆☆☆", "light": "全日照", "water": "每天浇水", "suitable": "阳台/可食用"},
],
"露台/庭院": [
{"name": "绣球", "difficulty": "★★★☆☆", "light": "半阴", "water": "保持湿润", "suitable": "露台/观花"},
{"name": "铁线莲", "difficulty": "★★★★☆", "light": "充足光照", "water": "见干见湿", "suitable": "庭院/攀援"},
]
}
def parse_garden_input(text: str) -> dict:
result = {"space": None, "location": None, "experience": "新手", "purpose": []}
if any(k in text for k in ["室内", "客厅", "卧室", "书房"]): result["space"] = "室内"
if any(k in text for k in ["阳台", "封闭阳台"]): result["space"] = "阳台"
if any(k in text for k in ["露台", "庭院", "院子", "花园"]): result["space"] = "露台/庭院"
if any(k in text for k in ["南向", "阳光充足"]): result["location"] = "南向"
if any(k in text for k in ["北向", "光线一般"]): result["location"] = "北向"
if any(k in text for k in ["新手", "第一次", "不会", "没经验"]): result["experience"] = "新手"
if any(k in text for k in ["食用", "蔬菜", "香草", "调料"]): result["purpose"].append("可食用")
if any(k in text for k in ["观花", "开花", "好看", "漂亮"]): result["purpose"].append("观花")
return result
def recommend_plants(parsed: dict) -> list:
if parsed["space"] == "室内":
return PLANTS["入门推荐"]
elif parsed["space"] == "阳台":
return PLANTS["阳台推荐"]
else:
return PLANTS["入门推荐"] + PLANTS["阳台推荐"]
def generate_garden_plan(parsed: dict) -> dict:
plants = recommend_plants(parsed)
return {
"recommendedPlants": plants[:4],
"layout_suggestion": f"{parsed['space'] or '阳台'}空间,建议采用'高低错落'布局:高处放喜光植物,低处放耐阴植物",
"seasonal_tips": {"春季": "换盆、施肥、病虫害预防", "夏季": "增加浇水频率、遮阴", "秋季": "修剪、准备越冬", "冬季": "减少浇水、入室保温"}
}
def handle(text: str) -> dict:
parsed = parse_garden_input(text)
plan = generate_garden_plan(parsed)
return {
"spaceType": parsed["space"] or "阳台(默认)",
"location": parsed.get("location", "南向(假设)"),
"experienceLevel": parsed["experience"],
"gardenPlan": plan,
"tools": {"基础工具": ["花铲", "浇水壶", "手套"], "进阶工具": ["枝剪", "喷雾器", "土壤检测仪"]},
"commonMistakes": ["浇水过多(最常见死因)", "光照不对(买前先了解朝向)", "频繁换盆(植物需要适应期)"]
}
if __name__ == "__main__":
for tc in ["我想在封闭阳台种点东西,新手推荐", "南向露台可以种什么花"]:
r = handle(tc)
plants = [p["name"] for p in r["gardenPlan"]["recommendedPlants"]]
print(f"Input: {tc}\n -> 空间: {r['spaceType']} | 经验: {r['experienceLevel']}\n -> 推荐: {plants}\n -> 常见错误: {r['commonMistakes'][0]}\n")
FILE:skill.json
{
"name": "home-garden-planner",
"slug": "home-garden-planner",
"version": "0.1.0",
"description": "auto-generated skeleton",
"author": "Harry",
"tags": ["lifestyle"],
"handler": "handler.py"
}
Assist users in breaking tasks into Pomodoro sessions, creating focus timers with work/rest intervals and productivity tips.
# Pomodoro Focus Timer(番茄专注计时器)
## Overview
帮助用户将任务拆解为番茄工作节拍,提供专注计时方案。
## Trigger
- 番茄钟
- 专注工作
- 计时
- 学习/工作计划
## Workflow
1. 解析任务类型和目标时长
2. 生成番茄工作节拍方案
3. 提供专注技巧和休息建议
## Output
JSON: {task, pomodoroPlan{sessions[], summary{}}, timer_settings{}, message}
FILE:handler.py
"""Pomodoro Focus Timer - 番茄专注计时器"""
import json
import re
def parse_pomodoro_input(text):
result = {"task": None, "duration": 25, "break_duration": 5, "rounds": 4}
m = re.search(r'(\d+)\s*分钟', text)
if m: result["duration"] = int(m.group(1))
skip_kw = ["番茄", "计时", "专注", "工作"]
task = text
for kw in skip_kw: task = task.replace(kw, "")
task = re.sub(r'\d+\s*分钟', '', task)
result["task"] = task.strip()[:50]
return result
def generate_pomodoro_plan(task, duration, break_dur, rounds):
sessions = []
for r in range(rounds):
sessions.append({"session": r+1, "type": "focus", "duration_minutes": duration, "task": task if r == 0 else None})
if r < rounds - 1:
sessions.append({"session": r+1, "type": "break", "duration_minutes": break_dur, "activity": "起身活动、喝水、眺望远处"})
return {"sessions": sessions, "summary": {"total_focus_minutes": duration*rounds}}
def handle(text):
parsed = parse_pomodoro_input(text)
plan = generate_pomodoro_plan(parsed["task"] or "未命名任务", parsed["duration"], parsed["break_duration"], parsed["rounds"])
return {"task": parsed["task"] or "未命名任务", "pomodoroPlan": plan, "timer_settings": {"focus_duration": parsed["duration"]}, "message": "番茄钟已设置"}
if __name__ == "__main__":
tc = "我要写报告,25分钟一个番茄"
r = handle(tc)
print(f'OK: task={r["task"]}, duration={r["timer_settings"]["focus_duration"]}min')
FILE:skill.json
{
"name": "pomodoro-focus-timer",
"slug": "pomodoro-focus-timer",
"version": "0.1.0",
"description": "auto-generated skeleton",
"author": "Harry",
"tags": ["lifestyle"],
"handler": "handler.py"
}
Analyze family conflicts calmly and provide mediation steps, dialogue suggestions, and resources for non-emergency situations.
# Family Conflict Mediator(家庭冲突调解员)
## Overview
帮助用户在家庭冲突(非紧急)情况下冷静分析,给出调解步骤和话术建议。
## Trigger
- 家庭争吵
- 夫妻冷战
- 亲子冲突
- 家庭矛盾
## Output
JSON: {detectedParties[], conflictLevel, mediationSteps[], principle, crisisResources{}}
FILE:handler.py
"""Family Conflict Mediator - 家庭冲突调解员"""
import json
import re
def parse_conflict(text: str) -> dict:
parties = []
if any(k in text for k in ["夫妻", "老婆", "老公", "伴侣"]): parties.append("伴侣")
if any(k in text for k in ["孩子", "儿子", "女儿", "亲子"]): parties.append("子女")
if any(k in text for k in ["父母", "婆婆", "岳母", "公婆"]): parties.append("长辈")
parties = parties or ["家庭成员"]
return {"parties": parties, "raw_text": text[:100]}
def assess_conflict_level(text: str) -> str:
if any(k in text for k in ["大打出手", "暴力", "报警", "砸东西"]): return "high"
if any(k in text for k in ["冷战", "分房", "离婚", "不过了"]): return "medium-high"
if any(k in text for k in ["争吵", "生气", "矛盾", "冲突"]): return "medium"
return "low"
def generate_mediation_steps(parties: list, level: str) -> dict:
steps = [
{"phase": "暂停", "duration": "等24-48小时", "action": "双方暂停直接对话,给情绪冷静时间"},
{"phase": "降温", "duration": "冷静期", "action": "各自写下:对方让我不舒服的具体行为 + 我的感受"},
{"phase": "复盘", "duration": "冷静后", "action": "用我感到XX当你说/做XX时格式沟通,避免指责"},
{"phase": "约定", "duration": "对话后", "action": "明确双方都能接受的解决方案,写下来"},
]
if level == "high":
steps.insert(0, {"phase": "安全第一", "duration": "立即", "action": "如有身体暴力风险,优先保证人身安全,必要时联系外部帮助"})
return {"steps": steps, "mediation_principle": "调解的目标不是分对错,而是找到双方都能接受的共存方式"}
def handle(text: str) -> dict:
parsed = parse_conflict(text)
level = assess_conflict_level(text)
mediation = generate_mediation_steps(parsed["parties"], level)
return {
"detectedParties": parsed["parties"],
"conflictLevel": level,
"mediationSteps": mediation["steps"],
"principle": mediation["mediation_principle"],
"crisisResources": {
"报警": "110",
"心理援助": "全国心理援助热线:400-161-9995",
"家暴热线": "12338(妇联热线)"
} if level in ["high", "medium-high"] else {}
}
if __name__ == "__main__":
for tc in ["和老婆冷战3天了怎么办", "和青春期孩子大吵了一架"]:
r = handle(tc)
print(f"Input: {tc}\n -> 当事人: {r['detectedParties']} | 等级: {r['conflictLevel']}\n -> 第一步: {r['mediationSteps'][0]['action']}\n")
FILE:skill.json
{
"name": "family-conflict-mediator",
"slug": "family-conflict-mediator",
"version": "0.1.0",
"description": "auto-generated skeleton",
"author": "Harry",
"tags": ["lifestyle"],
"handler": "handler.py"
}