rebly-sections 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/data/block-patterns.csv +160 -0
- package/assets/data/component-library/GDPR-modal.liquid +183 -0
- package/assets/data/component-library/Ishi_parallaxblockstyle1.liquid +331 -0
- package/assets/data/component-library/_index.csv +157 -19
- package/assets/data/component-library/about.liquid +1557 -0
- package/assets/data/component-library/adv-header.liquid +344 -0
- package/assets/data/component-library/adv-navigation.liquid +542 -0
- package/assets/data/component-library/announcement-bar.liquid +42 -60
- package/assets/data/component-library/article.liquid +242 -0
- package/assets/data/component-library/axeo-perfume-cosmetics-store-shopify-theme-about.liquid +1557 -0
- package/assets/data/component-library/basel-gl_newsletter_pets.liquid +612 -0
- package/assets/data/component-library/bixbang-fullpackage-collection-template.liquid +990 -0
- package/assets/data/component-library/blog-sidebar-article.liquid +51 -0
- package/assets/data/component-library/blog-sidebar-deals.liquid +189 -0
- package/assets/data/component-library/blog-sidebar-instagram.liquid +126 -0
- package/assets/data/component-library/blog-sidebar-tags.liquid +30 -0
- package/assets/data/component-library/blog.liquid +371 -0
- package/assets/data/component-library/brands-page.liquid +114 -0
- package/assets/data/component-library/cake-shop-shopify-theme-for-bakery-and-cafe-home-support-blo.liquid +780 -0
- package/assets/data/component-library/collection-template-promotion.liquid +1139 -0
- package/assets/data/component-library/collection-template.liquid +146 -0
- package/assets/data/component-library/contact-us.liquid +663 -0
- package/assets/data/component-library/contact.liquid +256 -0
- package/assets/data/component-library/copyright_payment.liquid +95 -0
- package/assets/data/component-library/custom-content.liquid +832 -0
- package/assets/data/component-library/faq-template-3.liquid +1014 -0
- package/assets/data/component-library/footer-model-1.liquid +503 -0
- package/assets/data/component-library/footer-model-10.liquid +210 -0
- package/assets/data/component-library/footer-model-2.liquid +460 -0
- package/assets/data/component-library/footer-model-3.liquid +548 -0
- package/assets/data/component-library/footer-model-4.liquid +455 -0
- package/assets/data/component-library/footer-model-5.liquid +407 -0
- package/assets/data/component-library/footer-model-6.liquid +543 -0
- package/assets/data/component-library/footer-model-7.liquid +345 -0
- package/assets/data/component-library/footer-model-8.liquid +279 -0
- package/assets/data/component-library/footer-model-9.liquid +376 -0
- package/assets/data/component-library/gallery.liquid +236 -0
- package/assets/data/component-library/gecko-shopify-v5-7-6-nulled-manual_blog.liquid +720 -0
- package/assets/data/component-library/gl_newsletter_pets.liquid +612 -0
- package/assets/data/component-library/gp-logo-list.liquid +362 -0
- package/assets/data/component-library/grid-banner-type-3-b.liquid +655 -0
- package/assets/data/component-library/header-model-1.liquid +427 -0
- package/assets/data/component-library/header-model-10.liquid +599 -0
- package/assets/data/component-library/header-model-2.liquid +633 -0
- package/assets/data/component-library/header-model-3.liquid +415 -0
- package/assets/data/component-library/header-model-4.liquid +754 -0
- package/assets/data/component-library/header-model-5.liquid +562 -0
- package/assets/data/component-library/header-model-6.liquid +713 -0
- package/assets/data/component-library/header-model-7.liquid +743 -0
- package/assets/data/component-library/header-model-8.liquid +500 -0
- package/assets/data/component-library/header-model-9.liquid +506 -0
- package/assets/data/component-library/home-blog-posts-1.liquid +399 -0
- package/assets/data/component-library/home-blog-posts-2.liquid +393 -0
- package/assets/data/component-library/home-blog-posts-3.liquid +545 -0
- package/assets/data/component-library/home-brand-slider.liquid +224 -0
- package/assets/data/component-library/home-circled-block.liquid +332 -0
- package/assets/data/component-library/home-contact-block-1.liquid +395 -0
- package/assets/data/component-library/home-contact-block-2.liquid +372 -0
- package/assets/data/component-library/home-content-block-1.liquid +320 -0
- package/assets/data/component-library/home-donut-chart.liquid +335 -0
- package/assets/data/component-library/home-fade-in-banner.liquid +277 -0
- package/assets/data/component-library/home-faq-model.liquid +323 -0
- package/assets/data/component-library/home-featured-blog.liquid +1462 -0
- package/assets/data/component-library/home-featured-collections.liquid +484 -0
- package/assets/data/component-library/home-gallery-block1.liquid +276 -0
- package/assets/data/component-library/home-gallery-block2.liquid +396 -0
- package/assets/data/component-library/home-grid-banner-type-1.liquid +371 -0
- package/assets/data/component-library/home-grid-banner-type-2.liquid +362 -0
- package/assets/data/component-library/home-grid-banner-type-3.liquid +374 -0
- package/assets/data/component-library/home-grid-banner-type-4.liquid +900 -0
- package/assets/data/component-library/home-grid-banner-type-5.liquid +368 -0
- package/assets/data/component-library/home-grid-banner-type-6.liquid +382 -0
- package/assets/data/component-library/home-grid-banner-type-7.liquid +371 -0
- package/assets/data/component-library/home-hotspot-with-product-carousel.liquid +1425 -0
- package/assets/data/component-library/home-image-gallery.liquid +1087 -0
- package/assets/data/component-library/home-instagram.liquid +356 -0
- package/assets/data/component-library/home-newsletter.liquid +246 -0
- package/assets/data/component-library/home-number-counter.liquid +790 -0
- package/assets/data/component-library/home-price-table.liquid +416 -0
- package/assets/data/component-library/home-pricing-table.liquid +1076 -0
- package/assets/data/component-library/home-product-grid.liquid +413 -0
- package/assets/data/component-library/home-product-tab-1.liquid +528 -0
- package/assets/data/component-library/home-product-tab-2.liquid +342 -0
- package/assets/data/component-library/home-product-tab-3.liquid +357 -0
- package/assets/data/component-library/home-product-vertical-carousel.liquid +477 -0
- package/assets/data/component-library/home-quotes-1.liquid +274 -0
- package/assets/data/component-library/home-quotes-2.liquid +239 -0
- package/assets/data/component-library/home-quotes-3.liquid +244 -0
- package/assets/data/component-library/home-quotes-4.liquid +258 -0
- package/assets/data/component-library/home-slider-width-promo-images.liquid +1377 -0
- package/assets/data/component-library/home-slideshow-type-1.liquid +656 -0
- package/assets/data/component-library/home-slideshow-type-2.liquid +570 -0
- package/assets/data/component-library/home-specification-block-1.liquid +468 -0
- package/assets/data/component-library/home-specification-block-2.liquid +291 -0
- package/assets/data/component-library/home-specification-block-3.liquid +429 -0
- package/assets/data/component-library/home-support-block.liquid +392 -0
- package/assets/data/component-library/home-testimonial.liquid +1348 -0
- package/assets/data/component-library/home-video-banner.liquid +317 -0
- package/assets/data/component-library/home-wide-banner.liquid +327 -0
- package/assets/data/component-library/icon-with-content.liquid +478 -0
- package/assets/data/component-library/instafeed.liquid +1 -0
- package/assets/data/component-library/kea-ecommerce-interior-furniture-shopify-theme-about.liquid +1300 -0
- package/assets/data/component-library/kidslife-responsive-shopify-theme-home-number-counter.liquid +729 -0
- package/assets/data/component-library/logo-bar.liquid +314 -0
- package/assets/data/component-library/lookbook.liquid +367 -0
- package/assets/data/component-library/manual_blog.liquid +724 -0
- package/assets/data/component-library/navigation-etc.liquid +642 -0
- package/assets/data/component-library/newsletter.liquid +246 -0
- package/assets/data/component-library/order-form.liquid +96 -0
- package/assets/data/component-library/page-catev1-template.liquid +344 -0
- package/assets/data/component-library/popup_video.liquid +396 -0
- package/assets/data/component-library/product-sidebar-bestsellers.liquid +99 -0
- package/assets/data/component-library/product-sidebar-deals.liquid +158 -0
- package/assets/data/component-library/product-template-2.liquid +629 -0
- package/assets/data/component-library/product-template-3.liquid +670 -0
- package/assets/data/component-library/product-template-4.liquid +627 -0
- package/assets/data/component-library/product-template-5.liquid +652 -0
- package/assets/data/component-library/product-template.liquid +698 -0
- package/assets/data/component-library/rich-text.liquid +541 -0
- package/assets/data/component-library/section-countdown-v2.liquid +215 -0
- package/assets/data/component-library/services.liquid +596 -0
- package/assets/data/component-library/shipping_info.liquid +327 -0
- package/assets/data/component-library/sidebar-bestsellers.liquid +109 -0
- package/assets/data/component-library/sidebar-category.liquid +105 -0
- package/assets/data/component-library/sidebar-colors.liquid +104 -0
- package/assets/data/component-library/single_product_feature.liquid +1892 -0
- package/assets/data/component-library/social-links-menu.liquid +244 -0
- package/assets/data/component-library/someone-purchased.liquid +190 -0
- package/assets/data/component-library/special-offer-area.liquid +530 -0
- package/assets/data/component-library/theno-minimal-clean-watch-store-shopify-theme-page-catev1-te.liquid +344 -0
- package/assets/data/component-library/top-bar-type-1.liquid +200 -0
- package/assets/data/component-library/top-bar-type-10.liquid +395 -0
- package/assets/data/component-library/top-bar-type-11.liquid +395 -0
- package/assets/data/component-library/top-bar-type-2.liquid +106 -0
- package/assets/data/component-library/top-bar-type-3.liquid +205 -0
- package/assets/data/component-library/top-countdown-bar.liquid +116 -0
- package/assets/data/component-library/trixe-solar-responsive-shopify-template-home-image-gallery.liquid +783 -0
- package/assets/data/component-library/trixe-solar-responsive-shopify-template-home-pricing-table.liquid +1043 -0
- package/assets/data/component-library/trixe-solar-responsive-shopify-template-home-testimonial.liquid +1338 -0
- package/assets/data/component-library/video.liquid +511 -0
- package/assets/data/component-library/waffy-spices-dry-fruits-store-shopify-theme-v-1-1-contact-us.liquid +523 -0
- package/assets/data/design-tokens.csv +93 -57
- package/assets/data/schema-library.csv +48 -46
- package/assets/data/settings-profiles.csv +235 -0
- package/assets/data/shopify-best-practices.csv +58 -36
- package/assets/scripts/backfill-component-index.py +102 -0
- package/assets/scripts/core.py +30 -8
- package/assets/scripts/fix-schema-library.py +42 -0
- package/assets/scripts/kb-analyzer-helpers.py +136 -0
- package/assets/scripts/kb-analyzer.py +186 -0
- package/assets/scripts/kb-builder.py +32 -63
- package/assets/scripts/kb-constants.py +62 -0
- package/assets/scripts/kb-extractor-helpers.py +178 -0
- package/assets/scripts/kb-extractor.py +106 -170
- package/assets/scripts/kb-synthesizer.py +251 -0
- package/assets/scripts/quality-gate-checks.py +55 -0
- package/assets/scripts/quality-gate.py +56 -2
- package/assets/scripts/section-generator-helpers.py +74 -0
- package/assets/scripts/section-generator.py +59 -49
- package/assets/templates/generation-prompt.md +78 -14
- package/package.json +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
KB Synthesizer — generate block-patterns.csv, settings-profiles.csv from aggregate-stats.json,
|
|
4
|
+
and curate top components from analysis.jsonl into component-library.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python3 kb-synthesizer.py --stats themes/aggregate-stats.json \
|
|
8
|
+
--analysis themes/analysis.jsonl --extracted themes/extracted/ \
|
|
9
|
+
--kb src/rebly-sections/data/
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import csv
|
|
14
|
+
import json
|
|
15
|
+
import shutil
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
MIN_CATEGORY_SECTIONS = 5 # skip categories with fewer than this
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def generate_block_patterns(stats: dict, analysis_path: Path, out_path: Path):
|
|
24
|
+
"""Generate block-patterns.csv from aggregate stats + analysis examples."""
|
|
25
|
+
rows = []
|
|
26
|
+
# Build lookup: find first example of each block combo per category
|
|
27
|
+
examples = {} # (category, combo) -> first JSON example from JSONL
|
|
28
|
+
with open(analysis_path, 'r', encoding='utf-8') as f:
|
|
29
|
+
for line in f:
|
|
30
|
+
rec = json.loads(line.strip())
|
|
31
|
+
cat = rec.get('category', '')
|
|
32
|
+
combo = '|'.join(sorted(rec.get('block_types', [])))
|
|
33
|
+
if combo and (cat, combo) not in examples:
|
|
34
|
+
examples[(cat, combo)] = rec
|
|
35
|
+
|
|
36
|
+
for cat, data in sorted(stats['categories'].items()):
|
|
37
|
+
if data['count'] < MIN_CATEGORY_SECTIONS:
|
|
38
|
+
continue
|
|
39
|
+
for rank, bc in enumerate(data.get('top_block_combos', [])[:8], 1):
|
|
40
|
+
combo = bc['combo']
|
|
41
|
+
rec = examples.get((cat, combo), {})
|
|
42
|
+
# Build a minimal JSON example from the record
|
|
43
|
+
block_types = combo.split('|')
|
|
44
|
+
json_ex = json.dumps([{"type": bt, "name": bt.replace('-', ' ').title()}
|
|
45
|
+
for bt in block_types], ensure_ascii=False)
|
|
46
|
+
common_settings = '|'.join(rec.get('setting_ids', [])[:6])
|
|
47
|
+
use_case = f"{cat} section with {' + '.join(block_types)} blocks"
|
|
48
|
+
rows.append({
|
|
49
|
+
'Category': cat,
|
|
50
|
+
'Block Type': combo,
|
|
51
|
+
'Common Settings': common_settings,
|
|
52
|
+
'Use Case': use_case,
|
|
53
|
+
'JSON Example': json_ex[:300],
|
|
54
|
+
'Frequency Rank': rank,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
fieldnames = ['Category', 'Block Type', 'Common Settings', 'Use Case', 'JSON Example', 'Frequency Rank']
|
|
58
|
+
with open(out_path, 'w', newline='', encoding='utf-8') as f:
|
|
59
|
+
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
|
60
|
+
writer.writeheader()
|
|
61
|
+
writer.writerows(rows)
|
|
62
|
+
print(f"block-patterns.csv: {len(rows)} rows")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def generate_settings_profiles(stats: dict, out_path: Path):
|
|
66
|
+
"""Generate settings-profiles.csv from aggregate stats top setting IDs."""
|
|
67
|
+
rows = []
|
|
68
|
+
for cat, data in sorted(stats['categories'].items()):
|
|
69
|
+
if data['count'] < MIN_CATEGORY_SECTIONS:
|
|
70
|
+
continue
|
|
71
|
+
# Build type lookup from top_setting_types
|
|
72
|
+
type_lookup = {t['type']: t for t in data.get('top_setting_types', [])}
|
|
73
|
+
|
|
74
|
+
for rank, sid_entry in enumerate(data.get('top_setting_ids', [])[:10], 1):
|
|
75
|
+
sid = sid_entry['id']
|
|
76
|
+
# Infer setting type from ID name patterns
|
|
77
|
+
stype = _infer_setting_type(sid, type_lookup)
|
|
78
|
+
label = sid.replace('_', ' ').replace('-', ' ').title()
|
|
79
|
+
required = rank <= 3 # top 3 are "required"
|
|
80
|
+
rows.append({
|
|
81
|
+
'Section Type': cat,
|
|
82
|
+
'Setting ID': sid,
|
|
83
|
+
'Setting Type': stype,
|
|
84
|
+
'Label': label,
|
|
85
|
+
'Default': '',
|
|
86
|
+
'Required': str(required),
|
|
87
|
+
'Notes': f"Appears in {sid_entry['count']}/{data['count']} {cat} sections",
|
|
88
|
+
'Frequency Rank': rank,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
fieldnames = ['Section Type', 'Setting ID', 'Setting Type', 'Label',
|
|
92
|
+
'Default', 'Required', 'Notes', 'Frequency Rank']
|
|
93
|
+
with open(out_path, 'w', newline='', encoding='utf-8') as f:
|
|
94
|
+
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
|
95
|
+
writer.writeheader()
|
|
96
|
+
writer.writerows(rows)
|
|
97
|
+
print(f"settings-profiles.csv: {len(rows)} rows")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _infer_setting_type(setting_id: str, type_lookup: dict) -> str:
|
|
101
|
+
"""Infer Shopify setting type from ID name."""
|
|
102
|
+
sid = setting_id.lower()
|
|
103
|
+
if 'image' in sid or 'photo' in sid or 'logo' in sid:
|
|
104
|
+
return 'image_picker'
|
|
105
|
+
if 'color' in sid and 'scheme' in sid:
|
|
106
|
+
return 'color_scheme'
|
|
107
|
+
if 'color' in sid:
|
|
108
|
+
return 'color'
|
|
109
|
+
if 'url' in sid or 'link' in sid:
|
|
110
|
+
return 'url'
|
|
111
|
+
if 'video' in sid:
|
|
112
|
+
return 'video_url'
|
|
113
|
+
if 'collection' in sid:
|
|
114
|
+
return 'collection'
|
|
115
|
+
if 'product' in sid:
|
|
116
|
+
return 'product'
|
|
117
|
+
if 'blog' in sid:
|
|
118
|
+
return 'blog'
|
|
119
|
+
if 'padding' in sid or 'margin' in sid or 'spacing' in sid or 'width' in sid or 'height' in sid:
|
|
120
|
+
return 'range'
|
|
121
|
+
if 'columns' in sid or 'count' in sid or 'limit' in sid or 'per_row' in sid:
|
|
122
|
+
return 'range'
|
|
123
|
+
if 'show' in sid or 'enable' in sid or 'hide' in sid:
|
|
124
|
+
return 'checkbox'
|
|
125
|
+
if 'layout' in sid or 'style' in sid or 'alignment' in sid or 'position' in sid:
|
|
126
|
+
return 'select'
|
|
127
|
+
if 'content' in sid or 'description' in sid or ('text' in sid and len(sid) > 6):
|
|
128
|
+
return 'richtext'
|
|
129
|
+
if 'heading' in sid or 'title' in sid or 'label' in sid or 'button' in sid:
|
|
130
|
+
return 'text'
|
|
131
|
+
if 'font' in sid:
|
|
132
|
+
return 'font_picker'
|
|
133
|
+
# Check if type_lookup has a dominant type
|
|
134
|
+
if type_lookup:
|
|
135
|
+
top = max(type_lookup.values(), key=lambda t: t.get('count', 0))
|
|
136
|
+
return top.get('type', 'text')
|
|
137
|
+
return 'text'
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def select_best_components(analysis_path: Path, extracted_dir: Path,
|
|
141
|
+
component_dir: Path, index_path: Path):
|
|
142
|
+
"""Pick top-scoring sections per category, copy to component library."""
|
|
143
|
+
# Score each section
|
|
144
|
+
scores = {} # (category, theme_slug, filename) -> score
|
|
145
|
+
records = {}
|
|
146
|
+
with open(analysis_path, 'r', encoding='utf-8') as f:
|
|
147
|
+
for line in f:
|
|
148
|
+
rec = json.loads(line.strip())
|
|
149
|
+
cat = rec.get('category', 'other')
|
|
150
|
+
key = (cat, rec['theme_slug'], rec['filename'])
|
|
151
|
+
score = rec.get('settings_count', 0) * 2
|
|
152
|
+
if rec.get('has_presets'):
|
|
153
|
+
score += 10
|
|
154
|
+
if rec.get('has_app_block'):
|
|
155
|
+
score += 5
|
|
156
|
+
if rec.get('blocks_count', 0) > 0:
|
|
157
|
+
score += 5
|
|
158
|
+
scores[key] = score
|
|
159
|
+
records[key] = rec
|
|
160
|
+
|
|
161
|
+
# Group by category, pick top 2 per category
|
|
162
|
+
from collections import defaultdict
|
|
163
|
+
by_cat = defaultdict(list)
|
|
164
|
+
for key, score in scores.items():
|
|
165
|
+
by_cat[key[0]].append((score, key))
|
|
166
|
+
|
|
167
|
+
# Load existing index
|
|
168
|
+
existing_slugs = set()
|
|
169
|
+
if index_path.exists():
|
|
170
|
+
with open(index_path, newline='', encoding='utf-8') as f:
|
|
171
|
+
for row in csv.DictReader(f):
|
|
172
|
+
existing_slugs.add(row.get('Slug', ''))
|
|
173
|
+
|
|
174
|
+
added = 0
|
|
175
|
+
new_rows = []
|
|
176
|
+
max_no = len(existing_slugs) + 1
|
|
177
|
+
|
|
178
|
+
for cat, entries in sorted(by_cat.items()):
|
|
179
|
+
if cat in ('other', 'header', 'footer', 'sidebar'):
|
|
180
|
+
continue # skip non-section categories
|
|
181
|
+
entries.sort(reverse=True)
|
|
182
|
+
for score, (_, theme_slug, filename) in entries[:2]:
|
|
183
|
+
slug = filename.replace('.liquid', '')
|
|
184
|
+
# Avoid slug collisions with existing files
|
|
185
|
+
if slug in existing_slugs:
|
|
186
|
+
slug = f"{theme_slug}-{slug}"[:60]
|
|
187
|
+
if slug in existing_slugs:
|
|
188
|
+
continue
|
|
189
|
+
|
|
190
|
+
src = extracted_dir / theme_slug / 'sections' / filename
|
|
191
|
+
if not src.exists():
|
|
192
|
+
continue
|
|
193
|
+
dst = component_dir / f"{slug}.liquid"
|
|
194
|
+
shutil.copy2(src, dst)
|
|
195
|
+
existing_slugs.add(slug)
|
|
196
|
+
|
|
197
|
+
rec = records[(cat, theme_slug, filename)]
|
|
198
|
+
new_rows.append({
|
|
199
|
+
'No': max_no,
|
|
200
|
+
'Name': rec.get('schema_name', slug),
|
|
201
|
+
'Slug': slug,
|
|
202
|
+
'Category': cat,
|
|
203
|
+
'Keywords': f"{slug.replace('-', ' ')} {cat}",
|
|
204
|
+
'Difficulty': 'intermediate',
|
|
205
|
+
'Blocks Used': '|'.join(rec.get('block_types', []))[:80],
|
|
206
|
+
'Settings Count': rec.get('settings_count', 0),
|
|
207
|
+
'ui_style_keywords': f"{slug.replace('-', ' ')} {cat}",
|
|
208
|
+
'File': f"{slug}.liquid",
|
|
209
|
+
'Description': f"{rec.get('schema_name', slug)} section from {theme_slug}",
|
|
210
|
+
'Completeness': score,
|
|
211
|
+
'Has Presets': str(rec.get('has_presets', False)),
|
|
212
|
+
'Has App Block': str(rec.get('has_app_block', False)),
|
|
213
|
+
})
|
|
214
|
+
max_no += 1
|
|
215
|
+
added += 1
|
|
216
|
+
|
|
217
|
+
# Append to index
|
|
218
|
+
if new_rows:
|
|
219
|
+
fieldnames = list(new_rows[0].keys())
|
|
220
|
+
with open(index_path, 'a', newline='', encoding='utf-8') as f:
|
|
221
|
+
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
|
222
|
+
for row in new_rows:
|
|
223
|
+
writer.writerow(row)
|
|
224
|
+
|
|
225
|
+
print(f"Components: {added} new curated files added to library")
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def main():
|
|
229
|
+
parser = argparse.ArgumentParser(description="Synthesize KB from analysis data")
|
|
230
|
+
parser.add_argument('--stats', default='themes/aggregate-stats.json')
|
|
231
|
+
parser.add_argument('--analysis', default='themes/analysis.jsonl')
|
|
232
|
+
parser.add_argument('--extracted', default='themes/extracted/')
|
|
233
|
+
parser.add_argument('--kb', default='src/rebly-sections/data/')
|
|
234
|
+
parser.add_argument('--mode', default='all', choices=['all', 'blocks', 'settings', 'components'])
|
|
235
|
+
args = parser.parse_args()
|
|
236
|
+
|
|
237
|
+
stats = json.loads(Path(args.stats).read_text(encoding='utf-8'))
|
|
238
|
+
kb_dir = Path(args.kb)
|
|
239
|
+
|
|
240
|
+
if args.mode in ('all', 'blocks'):
|
|
241
|
+
generate_block_patterns(stats, Path(args.analysis), kb_dir / 'block-patterns.csv')
|
|
242
|
+
if args.mode in ('all', 'settings'):
|
|
243
|
+
generate_settings_profiles(stats, kb_dir / 'settings-profiles.csv')
|
|
244
|
+
if args.mode in ('all', 'components'):
|
|
245
|
+
select_best_components(
|
|
246
|
+
Path(args.analysis), Path(args.extracted),
|
|
247
|
+
kb_dir / 'component-library', kb_dir / 'component-library' / '_index.csv')
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
if __name__ == '__main__':
|
|
251
|
+
main()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Additional quality gate checks — WARN-level advisory checks for section quality."""
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def check_has_color_scheme(content, schema_json):
|
|
8
|
+
"""Check if section schema includes color_scheme setting."""
|
|
9
|
+
if not schema_json:
|
|
10
|
+
return ("WARN", "No schema to check for color_scheme")
|
|
11
|
+
settings = schema_json.get("settings", [])
|
|
12
|
+
has_cs = any(s.get("type") == "color_scheme" for s in settings)
|
|
13
|
+
if has_cs:
|
|
14
|
+
return ("PASS", "color_scheme setting present")
|
|
15
|
+
return ("WARN", "Missing color_scheme setting — recommended for theme consistency")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def check_has_font_picker(content, schema_json):
|
|
19
|
+
"""Check if section schema includes at least one font_picker."""
|
|
20
|
+
if not schema_json:
|
|
21
|
+
return ("WARN", "No schema to check for font_picker")
|
|
22
|
+
settings = schema_json.get("settings", [])
|
|
23
|
+
has_fp = any(s.get("type") == "font_picker" for s in settings)
|
|
24
|
+
if has_fp:
|
|
25
|
+
return ("PASS", "font_picker setting present")
|
|
26
|
+
return ("WARN", "Missing font_picker — sections with headings should include typography control")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def check_has_heading_tag(content, schema_json):
|
|
30
|
+
"""Check if section has configurable heading tag for SEO."""
|
|
31
|
+
if not schema_json:
|
|
32
|
+
return ("WARN", "No schema to check for heading_tag")
|
|
33
|
+
settings = schema_json.get("settings", [])
|
|
34
|
+
has_ht = any(
|
|
35
|
+
s.get("id") in ("heading_tag", "heading_element", "tag")
|
|
36
|
+
and s.get("type") == "select"
|
|
37
|
+
for s in settings
|
|
38
|
+
)
|
|
39
|
+
if has_ht:
|
|
40
|
+
return ("PASS", "Heading tag configurable")
|
|
41
|
+
return ("WARN", "Missing heading_tag select — heading level should be merchant-configurable")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def check_no_hardcoded_colors(content, schema_json):
|
|
45
|
+
"""Check CSS doesn't contain hardcoded hex colors (outside Liquid variables)."""
|
|
46
|
+
style_match = re.search(r'<style[^>]*>(.*?)</style>', content, re.DOTALL)
|
|
47
|
+
if not style_match:
|
|
48
|
+
return ("PASS", "No style block found")
|
|
49
|
+
css = style_match.group(1)
|
|
50
|
+
# Strip Liquid variable references ({{ ... }}) before scanning
|
|
51
|
+
css_no_liquid = re.sub(r'\{\{.*?\}\}', '', css)
|
|
52
|
+
hex_colors = re.findall(r'#[0-9a-fA-F]{3,8}\b', css_no_liquid)
|
|
53
|
+
if hex_colors:
|
|
54
|
+
return ("WARN", f"Hardcoded colors in CSS: {', '.join(hex_colors[:5])} — use CSS variables instead")
|
|
55
|
+
return ("PASS", "No hardcoded colors in CSS")
|
|
@@ -6,12 +6,23 @@ Usage: python3 quality-gate.py path/to/section.liquid [--json]
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import argparse
|
|
9
|
+
import importlib.util
|
|
9
10
|
import json
|
|
10
11
|
import re
|
|
11
12
|
import sys
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
# Import advisory checks from helper module (graceful fallback if missing)
|
|
16
|
+
_checks_mod = None
|
|
17
|
+
try:
|
|
18
|
+
_checks_path = Path(__file__).parent / 'quality-gate-checks.py'
|
|
19
|
+
_spec = importlib.util.spec_from_file_location('qg_checks', _checks_path)
|
|
20
|
+
_checks_mod = importlib.util.module_from_spec(_spec)
|
|
21
|
+
_spec.loader.exec_module(_checks_mod)
|
|
22
|
+
except (FileNotFoundError, AttributeError):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
# Core checks (PASS/FAIL)
|
|
15
26
|
QUALITY_CHECKS = [
|
|
16
27
|
("schema_valid_json", "Schema block contains valid JSON"),
|
|
17
28
|
("has_presets", "Schema has non-empty presets array"),
|
|
@@ -26,6 +37,14 @@ QUALITY_CHECKS = [
|
|
|
26
37
|
("text_elements_explicit_color", "Text elements have explicit color in CSS"),
|
|
27
38
|
]
|
|
28
39
|
|
|
40
|
+
# Advisory checks (PASS/WARN) — imported from quality-gate-checks.py
|
|
41
|
+
ADVISORY_CHECKS = [
|
|
42
|
+
("has_color_scheme", "color_scheme setting present"),
|
|
43
|
+
("has_font_picker", "font_picker setting present"),
|
|
44
|
+
("has_heading_tag", "Heading tag configurable via select"),
|
|
45
|
+
("no_hardcoded_colors", "No hardcoded hex colors in CSS"),
|
|
46
|
+
]
|
|
47
|
+
|
|
29
48
|
|
|
30
49
|
def extract_schema_json(content):
|
|
31
50
|
"""Extract JSON from {% schema %} block"""
|
|
@@ -161,10 +180,13 @@ def run_quality_gate(filepath):
|
|
|
161
180
|
return f"Error: File not found: {filepath}"
|
|
162
181
|
|
|
163
182
|
content = path.read_text(encoding="utf-8")
|
|
183
|
+
schema_json = extract_schema_json(content)
|
|
164
184
|
results = []
|
|
165
185
|
passed = 0
|
|
166
186
|
failed = 0
|
|
187
|
+
warned = 0
|
|
167
188
|
|
|
189
|
+
# Core checks (PASS/FAIL)
|
|
168
190
|
for check_id, description in QUALITY_CHECKS:
|
|
169
191
|
check_fn = CHECK_FUNCTIONS[check_id]
|
|
170
192
|
try:
|
|
@@ -179,12 +201,44 @@ def run_quality_gate(filepath):
|
|
|
179
201
|
failed += 1
|
|
180
202
|
results.append(f"✗ {description}: ERROR ({e})")
|
|
181
203
|
|
|
204
|
+
# Advisory checks (PASS/WARN) — skip if helper module unavailable
|
|
205
|
+
if not _checks_mod:
|
|
206
|
+
advisory_fns = {}
|
|
207
|
+
else:
|
|
208
|
+
advisory_fns = {
|
|
209
|
+
"has_color_scheme": _checks_mod.check_has_color_scheme,
|
|
210
|
+
"has_font_picker": _checks_mod.check_has_font_picker,
|
|
211
|
+
"has_heading_tag": _checks_mod.check_has_heading_tag,
|
|
212
|
+
"no_hardcoded_colors": _checks_mod.check_no_hardcoded_colors,
|
|
213
|
+
}
|
|
214
|
+
for check_id, description in ADVISORY_CHECKS:
|
|
215
|
+
if check_id not in advisory_fns:
|
|
216
|
+
continue
|
|
217
|
+
check_fn = advisory_fns[check_id]
|
|
218
|
+
try:
|
|
219
|
+
status, message = check_fn(content, schema_json)
|
|
220
|
+
if status == "PASS":
|
|
221
|
+
passed += 1
|
|
222
|
+
else:
|
|
223
|
+
warned += 1
|
|
224
|
+
icon = "✓" if status == "PASS" else "⚠"
|
|
225
|
+
results.append(f"{icon} {message}: {status}")
|
|
226
|
+
except Exception as e:
|
|
227
|
+
warned += 1
|
|
228
|
+
results.append(f"⚠ {description}: ERROR ({e})")
|
|
229
|
+
|
|
230
|
+
total = passed + failed + warned
|
|
182
231
|
summary = f"\n## Quality Gate: {path.name}\n"
|
|
183
|
-
summary += f"**Score:** {passed}/{
|
|
232
|
+
summary += f"**Score:** {passed}/{total} checks passed"
|
|
233
|
+
if warned:
|
|
234
|
+
summary += f" ({warned} warnings)"
|
|
235
|
+
summary += "\n\n"
|
|
184
236
|
summary += "\n".join(results)
|
|
185
237
|
|
|
186
238
|
if failed == 0:
|
|
187
239
|
summary += "\n\n**Result: PASS** ✓"
|
|
240
|
+
if warned:
|
|
241
|
+
summary += f" (with {warned} advisory warnings)"
|
|
188
242
|
else:
|
|
189
243
|
summary += f"\n\n**Result: FAIL** — {failed} issue(s) found"
|
|
190
244
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Helper search functions for section-generator.py — block patterns, settings profiles, theme DNA."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
_scripts_dir = str(Path(__file__).parent)
|
|
8
|
+
if _scripts_dir not in sys.path:
|
|
9
|
+
sys.path.append(_scripts_dir)
|
|
10
|
+
from core import search
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def search_block_patterns(description):
|
|
14
|
+
"""Search block-patterns.csv for relevant block configurations."""
|
|
15
|
+
try:
|
|
16
|
+
result = search(description, domain="block-patterns", max_results=5)
|
|
17
|
+
if "error" in result or not result.get("results"):
|
|
18
|
+
return ""
|
|
19
|
+
lines = ["**Relevant Block Patterns:**"]
|
|
20
|
+
for r in result["results"]:
|
|
21
|
+
lines.append(f"- [{r.get('Category', '')} / {r.get('Block Type', '')}] {r.get('Use Case', '')}")
|
|
22
|
+
lines.append(f" Settings: {r.get('Common Settings', '')}")
|
|
23
|
+
ex = r.get('JSON Example', '')
|
|
24
|
+
if ex:
|
|
25
|
+
lines.append(f" Example: `{ex[:200]}`")
|
|
26
|
+
return "\n".join(lines)
|
|
27
|
+
except (FileNotFoundError, KeyError):
|
|
28
|
+
return ""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def search_settings_profile(section_name):
|
|
32
|
+
"""Search settings-profiles.csv for recommended settings for this section type."""
|
|
33
|
+
try:
|
|
34
|
+
result = search(section_name, domain="settings-profiles", max_results=8)
|
|
35
|
+
if "error" in result or not result.get("results"):
|
|
36
|
+
return ""
|
|
37
|
+
lines = ["**Recommended Settings Profile:**",
|
|
38
|
+
"| Setting ID | Type | Label | Default | Required |",
|
|
39
|
+
"|------------|------|-------|---------|----------|"]
|
|
40
|
+
for r in result["results"]:
|
|
41
|
+
lines.append(f"| {r.get('Setting ID', '')} | {r.get('Setting Type', '')} | "
|
|
42
|
+
f"{r.get('Label', '')} | {r.get('Default', '')} | {r.get('Required', '')} |")
|
|
43
|
+
return "\n".join(lines)
|
|
44
|
+
except (FileNotFoundError, KeyError):
|
|
45
|
+
return ""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def search_theme_dna(theme_name):
|
|
49
|
+
"""Search theme-dna.csv for theme-specific conventions."""
|
|
50
|
+
try:
|
|
51
|
+
result = search(theme_name, domain="themes", max_results=1)
|
|
52
|
+
if "error" in result or not result.get("results"):
|
|
53
|
+
return ""
|
|
54
|
+
r = result["results"][0]
|
|
55
|
+
return (f"**Theme DNA:** {r.get('Theme', '')} — "
|
|
56
|
+
f"CSS Prefix: {r.get('CSS Variable Prefix', '')} | "
|
|
57
|
+
f"Naming: {r.get('Section Naming', '')} | "
|
|
58
|
+
f"Blocks: {r.get('Block Conventions', '')}")
|
|
59
|
+
except (FileNotFoundError, KeyError):
|
|
60
|
+
return ""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def search_design_tokens(description):
|
|
64
|
+
"""Search design-tokens.csv for relevant CSS tokens."""
|
|
65
|
+
try:
|
|
66
|
+
result = search(description, domain="tokens", max_results=5, min_score=0.3)
|
|
67
|
+
if "error" in result or not result.get("results"):
|
|
68
|
+
return ""
|
|
69
|
+
lines = ["**Relevant Design Tokens:**"]
|
|
70
|
+
for r in result["results"]:
|
|
71
|
+
lines.append(f"- `{r.get('CSS Variable', '')}`: {r.get('Value', '')} — {r.get('Usage', '')}")
|
|
72
|
+
return "\n".join(lines)
|
|
73
|
+
except (FileNotFoundError, KeyError):
|
|
74
|
+
return ""
|
|
@@ -6,6 +6,7 @@ Usage: python3 section-generator.py "name" "description" [--theme-profile path]
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import argparse
|
|
9
|
+
import re
|
|
9
10
|
import sys
|
|
10
11
|
import io
|
|
11
12
|
from pathlib import Path
|
|
@@ -16,6 +17,17 @@ if _scripts_dir not in sys.path:
|
|
|
16
17
|
sys.path.append(_scripts_dir)
|
|
17
18
|
from core import search, search_component, DATA_DIR
|
|
18
19
|
|
|
20
|
+
# Import helpers (kebab-case filename requires importlib)
|
|
21
|
+
import importlib.util as _ilu
|
|
22
|
+
_hp = Path(__file__).parent / 'section-generator-helpers.py'
|
|
23
|
+
_hs = _ilu.spec_from_file_location('sg_helpers', _hp)
|
|
24
|
+
_hm = _ilu.module_from_spec(_hs)
|
|
25
|
+
_hs.loader.exec_module(_hm)
|
|
26
|
+
search_block_patterns = _hm.search_block_patterns
|
|
27
|
+
search_settings_profile = _hm.search_settings_profile
|
|
28
|
+
search_theme_dna = _hm.search_theme_dna
|
|
29
|
+
search_design_tokens = _hm.search_design_tokens
|
|
30
|
+
|
|
19
31
|
TEMPLATE_DIR = Path(__file__).parent.parent / "templates"
|
|
20
32
|
|
|
21
33
|
# UTF-8 stdout
|
|
@@ -38,8 +50,8 @@ def load_template(name):
|
|
|
38
50
|
return ""
|
|
39
51
|
|
|
40
52
|
|
|
41
|
-
def search_components(description, max_results=
|
|
42
|
-
"""Search component library for closest pattern"""
|
|
53
|
+
def search_components(description, max_results=1):
|
|
54
|
+
"""Search component library for closest pattern — 1 full untruncated example"""
|
|
43
55
|
result = search_component(description, max_results=max_results)
|
|
44
56
|
if "error" in result or not result.get("results"):
|
|
45
57
|
return "No matching component patterns found."
|
|
@@ -49,14 +61,14 @@ def search_components(description, max_results=2):
|
|
|
49
61
|
output.append(f"**{r.get('Name', 'Unknown')}** ({r.get('Category', '')}, {r.get('Difficulty', '')})")
|
|
50
62
|
output.append(f"Blocks: {r.get('Blocks Used', '')} | Settings: {r.get('Settings Count', '')}")
|
|
51
63
|
if 'liquid_content' in r:
|
|
52
|
-
output.append(f"```liquid\n{r['liquid_content'][:
|
|
64
|
+
output.append(f"```liquid\n{r['liquid_content'][:6000]}\n```")
|
|
53
65
|
output.append("")
|
|
54
66
|
return "\n".join(output)
|
|
55
67
|
|
|
56
68
|
|
|
57
69
|
def search_schema_settings(description, max_results=5):
|
|
58
70
|
"""Search schema-library for relevant setting types"""
|
|
59
|
-
result = search(description, domain="schema", max_results=max_results)
|
|
71
|
+
result = search(description, domain="schema", max_results=max_results, min_score=0.3)
|
|
60
72
|
if "error" in result or not result.get("results"):
|
|
61
73
|
return "No matching schema settings found."
|
|
62
74
|
|
|
@@ -71,7 +83,7 @@ def search_schema_settings(description, max_results=5):
|
|
|
71
83
|
|
|
72
84
|
def search_best_practices(description, max_results=5):
|
|
73
85
|
"""Search best practices for applicable rules"""
|
|
74
|
-
result = search(description, domain="practices", max_results=max_results)
|
|
86
|
+
result = search(description, domain="practices", max_results=max_results, min_score=0.3)
|
|
75
87
|
if "error" in result or not result.get("results"):
|
|
76
88
|
return "No matching best practices found."
|
|
77
89
|
|
|
@@ -88,7 +100,7 @@ def search_best_practices(description, max_results=5):
|
|
|
88
100
|
|
|
89
101
|
|
|
90
102
|
def try_ui_ux_bridge(ui_style_keywords):
|
|
91
|
-
"""Optional: query ui-ux-pro-max if available"""
|
|
103
|
+
"""Optional: query ui-ux-pro-max if available. Returns empty string if N/A."""
|
|
92
104
|
try:
|
|
93
105
|
ux_search_path = Path(__file__).parent.parent.parent.parent / ".claude" / "skills" / "ui-ux-pro-max" / "scripts" / "core.py"
|
|
94
106
|
if not ux_search_path.exists():
|
|
@@ -101,17 +113,27 @@ def try_ui_ux_bridge(ui_style_keywords):
|
|
|
101
113
|
spec.loader.exec_module(ux_module)
|
|
102
114
|
result = ux_module.search(ui_style_keywords, domain="style", max_results=2)
|
|
103
115
|
if result.get("results"):
|
|
104
|
-
lines = ["**UI/UX Style Recommendations
|
|
116
|
+
lines = ["<design_recommendations>", "**UI/UX Style Recommendations:**"]
|
|
105
117
|
for r in result["results"]:
|
|
106
118
|
lines.append(f"- {r.get('Style Category', '')}: {r.get('Keywords', '')}")
|
|
107
119
|
lines.append(f" Effects: {r.get('Effects & Animation', '')}")
|
|
120
|
+
lines.append("</design_recommendations>")
|
|
108
121
|
return "\n".join(lines)
|
|
109
122
|
except Exception:
|
|
110
123
|
pass
|
|
111
|
-
|
|
124
|
+
# Return empty string instead of N/A to avoid wasting prompt tokens
|
|
125
|
+
return ""
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _strip_empty_xml_tags(content):
|
|
129
|
+
"""Remove XML tags whose body is empty or whitespace-only"""
|
|
130
|
+
return re.sub(r'<([\w-]+)>\s*</\1>', '', content)
|
|
112
131
|
|
|
113
132
|
|
|
114
|
-
def assemble_context(name, description, theme_profile, component, schema,
|
|
133
|
+
def assemble_context(name, description, theme_profile, component, schema,
|
|
134
|
+
practices, design_recs, block_patterns="",
|
|
135
|
+
settings_profile="", theme_dna="",
|
|
136
|
+
design_tokens="", refinement=""):
|
|
115
137
|
"""Merge all context into generation prompt"""
|
|
116
138
|
template = load_template("generation-prompt.md")
|
|
117
139
|
base_template = load_template("section-base.liquid")
|
|
@@ -125,44 +147,19 @@ def assemble_context(name, description, theme_profile, component, schema, practi
|
|
|
125
147
|
content = content.replace("{{ schema_search_results }}", schema)
|
|
126
148
|
content = content.replace("{{ best_practices_results }}", practices)
|
|
127
149
|
content = content.replace("{{ ui_ux_recommendations }}", design_recs)
|
|
150
|
+
content = content.replace("{{ block_patterns_results }}", block_patterns)
|
|
151
|
+
content = content.replace("{{ settings_profile_results }}", settings_profile)
|
|
152
|
+
content = content.replace("{{ theme_dna_context }}", theme_dna)
|
|
153
|
+
content = content.replace("{{ design_tokens_results }}", design_tokens)
|
|
154
|
+
content = content.replace("{{ refinement_context }}", refinement)
|
|
128
155
|
base_block = f"```liquid\n{base_template}\n```" if base_template else "See standard OS2.0 section structure"
|
|
129
156
|
content = content.replace("{{ section_base_content }}", base_block)
|
|
157
|
+
# Clean up empty XML tags from unused sections
|
|
158
|
+
content = _strip_empty_xml_tags(content)
|
|
130
159
|
return content
|
|
131
160
|
|
|
132
|
-
# Fallback: manual assembly
|
|
133
|
-
return f"
|
|
134
|
-
|
|
135
|
-
## Description
|
|
136
|
-
{description}
|
|
137
|
-
|
|
138
|
-
## Theme Context
|
|
139
|
-
{theme_profile}
|
|
140
|
-
|
|
141
|
-
## Closest Pattern Template
|
|
142
|
-
{component}
|
|
143
|
-
|
|
144
|
-
## Relevant Schema Settings
|
|
145
|
-
{schema}
|
|
146
|
-
|
|
147
|
-
## Applicable Best Practices
|
|
148
|
-
{practices}
|
|
149
|
-
|
|
150
|
-
## Design Recommendations
|
|
151
|
-
{design_recs}
|
|
152
|
-
|
|
153
|
-
## Base Template
|
|
154
|
-
```liquid
|
|
155
|
-
{base_template}
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## Requirements
|
|
159
|
-
- OS2.0 compliant (presets, @app blocks, block.shopify_attributes)
|
|
160
|
-
- Scoped CSS via #section-{{{{ section.id }}}}
|
|
161
|
-
- Responsive (mobile-first)
|
|
162
|
-
- Accessible (semantic HTML, aria-labels, alt text)
|
|
163
|
-
- Performance (lazy-load non-hero images, no Liquid in CSS/JS)
|
|
164
|
-
- Use {{% render %}} not {{% include %}}
|
|
165
|
-
"""
|
|
161
|
+
# Fallback: manual assembly (shouldn't reach here with template present)
|
|
162
|
+
return f"# Generate Shopify Section: {name}\n\n{description}\n\n{component}\n\n{schema}\n\n{practices}"
|
|
166
163
|
|
|
167
164
|
|
|
168
165
|
def main():
|
|
@@ -174,11 +171,18 @@ def main():
|
|
|
174
171
|
args = parser.parse_args()
|
|
175
172
|
|
|
176
173
|
theme = load_theme_profile(args.theme_profile)
|
|
174
|
+
# Tailored BM25 queries per domain for better relevance
|
|
177
175
|
component = search_components(f"{args.name} {args.description}")
|
|
178
|
-
schema = search_schema_settings(args.description)
|
|
179
|
-
practices = search_best_practices(args.
|
|
176
|
+
schema = search_schema_settings(f"shopify section settings {args.description}")
|
|
177
|
+
practices = search_best_practices(f"shopify section {args.name} best practices")
|
|
180
178
|
design_recs = try_ui_ux_bridge(args.name + " " + args.description)
|
|
179
|
+
blocks = search_block_patterns(f"{args.name} {args.description}")
|
|
180
|
+
profile = search_settings_profile(args.name)
|
|
181
|
+
dna = search_theme_dna(args.name)
|
|
182
|
+
tokens = search_design_tokens(f"css typography spacing color {args.description}")
|
|
181
183
|
|
|
184
|
+
# Build refinement context as dedicated section (not appended to component)
|
|
185
|
+
refinement = ""
|
|
182
186
|
if args.refine:
|
|
183
187
|
refine_path = Path(args.refine)
|
|
184
188
|
try:
|
|
@@ -189,12 +193,18 @@ def main():
|
|
|
189
193
|
try:
|
|
190
194
|
from quality_gate import run_quality_gate
|
|
191
195
|
gate_result = run_quality_gate(args.refine)
|
|
192
|
-
|
|
193
|
-
|
|
196
|
+
refinement = (f"<refinement>\n## Existing Section (for refinement)\n"
|
|
197
|
+
f"```liquid\n{existing[:3000]}\n```\n\n"
|
|
198
|
+
f"## Quality Gate Results\n{gate_result}\n\n"
|
|
199
|
+
f"Please fix the issues identified above while preserving functionality.\n"
|
|
200
|
+
f"</refinement>")
|
|
194
201
|
except ImportError:
|
|
195
|
-
|
|
202
|
+
refinement = (f"<refinement>\n## Existing Section (for refinement)\n"
|
|
203
|
+
f"```liquid\n{existing[:3000]}\n```\n</refinement>")
|
|
196
204
|
|
|
197
|
-
context = assemble_context(args.name, args.description, theme, component, schema,
|
|
205
|
+
context = assemble_context(args.name, args.description, theme, component, schema,
|
|
206
|
+
practices, design_recs, blocks, profile, dna,
|
|
207
|
+
design_tokens=tokens, refinement=refinement)
|
|
198
208
|
print(context)
|
|
199
209
|
|
|
200
210
|
|