rebly-sections 1.1.0 → 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.
@@ -88,3 +88,6 @@ No,Token Name,Category,Value,CSS Variable,Keywords,Usage,Notes
88
88
  87,z-overlay,z-index,30,--z-overlay,z-index overlay backdrop,z-index: var(--z-overlay),Z-index layer scale
89
89
  88,z-modal,z-index,40,--z-modal,z-index modal dialog,z-index: var(--z-modal),Z-index layer scale
90
90
  89,z-toast,z-index,50,--z-toast,z-index toast notification alert,z-index: var(--z-toast),Z-index layer scale
91
+ 90,font-family-sans,typography,"system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",--font-family-sans,font family sans serif system stack body,font-family: var(--font-family-sans),System sans-serif stack for body text
92
+ 91,font-family-serif,typography,"Georgia, 'Times New Roman', serif",--font-family-serif,font family serif classic editorial heading,font-family: var(--font-family-serif),Classic serif stack for editorial headings
93
+ 92,font-family-mono,typography,"'SF Mono', 'Fira Code', 'Courier New', monospace",--font-family-mono,font family monospace code technical,font-family: var(--font-family-mono),Monospace stack for code and technical content
@@ -119,6 +119,10 @@ hero,title_1,text,Title 1,,False,Appears in 296/2150 hero sections,7
119
119
  hero,url_banner2,url,Url Banner2,,False,Appears in 276/2150 hero sections,8
120
120
  hero,button_2,text,Button 2,,False,Appears in 261/2150 hero sections,9
121
121
  hero,url_button1,url,Url Button1,,False,Appears in 254/2150 hero sections,10
122
+ hero,heading_font,font_picker,Heading Font,,False,Typography control for hero heading,11
123
+ hero,heading_size,select,Heading Size,,False,Small/Medium/Large heading scale,12
124
+ hero,heading_tag,select,Heading Tag,,False,H1/H2/H3 for SEO hierarchy,13
125
+ hero,color_scheme,color_scheme,Color Scheme,,False,Theme-wide color scheme,14
122
126
  logo,title,text,Title,,True,Appears in 19/32 logo sections,1
123
127
  logo,logo_max_width,image_picker,Logo Max Width,,True,Appears in 7/32 logo sections,2
124
128
  logo,logo,image_picker,Logo,,True,Appears in 6/32 logo sections,3
@@ -51,3 +51,8 @@ No,Rule,Category,Severity,Keywords,Description,Do,Dont,Code Example
51
51
  50,Structured data JSON-LD,seo,recommended,structured data schema.org json-ld rich snippets,"Add JSON-LD structured data for products, articles, FAQs","Include <script type=""application/ld+json""> with schema.org markup",Rely solely on meta tags without structured data,
52
52
  51,Heading per section,seo,important,heading h2 section title SEO crawl hierarchy,Each section should have a configurable heading element,Use a heading_tag setting (h2/h3/h4) for section headings,Hardcode heading levels — breaks hierarchy across sections,
53
53
  52,Image alt from settings,seo,important,image alt text setting SEO accessible,Provide alt text settings or use Shopify image.alt,"Output alt=""{{ section.settings.image.alt | escape }}""",Omit alt attributes on section images,
54
+ 53,Typography controls required,schema,high,"font_picker heading typography control",Sections with heading text should include font_picker setting,Include font_picker setting for primary heading,Hardcode font-family in CSS only,
55
+ 54,Heading tag configurable,seo,high,"heading tag h1 h2 h3 SEO hierarchy select",Heading level must be configurable via select setting for SEO,Add heading_tag select setting (h1/h2/h3/h4),Hardcode <h1> or <h2> without merchant control,
56
+ 55,Color scheme over color picker,os2-architecture,high,"color_scheme color picker theme consistency",Prefer color_scheme setting over individual color pickers for theme consistency,Use type: color_scheme for section colors,Use individual color pickers for every color,
57
+ 56,Button styling controls,schema,high,"button style color background CTA",CTA buttons should have configurable style/color settings,Add button_style select and button color settings,Hardcode button background and text colors,
58
+ 57,No hardcoded font sizes,css,high,"font size hardcoded clamp responsive typography",Font sizes should use clamp() or CSS vars — never fixed px,Use clamp() or CSS custom properties for font-size,Use fixed px values like font-size: 48px,
@@ -4,6 +4,7 @@
4
4
 
5
5
  import csv
6
6
  import re
7
+ import sys
7
8
  from pathlib import Path
8
9
  from math import log
9
10
  from collections import defaultdict
@@ -124,7 +125,7 @@ def _load_csv(filepath):
124
125
  return list(csv.DictReader(f))
125
126
 
126
127
 
127
- def _search_csv(filepath, search_cols, output_cols, query, max_results):
128
+ def _search_csv(filepath, search_cols, output_cols, query, max_results, min_score=0.0):
128
129
  """Core search function using BM25"""
129
130
  if not filepath.exists():
130
131
  return []
@@ -136,11 +137,20 @@ def _search_csv(filepath, search_cols, output_cols, query, max_results):
136
137
  bm25.fit(documents)
137
138
  ranked = bm25.score(query)
138
139
 
140
+ # Filter by min_score threshold
141
+ candidates = [(idx, score) for idx, score in ranked[:max_results * 2] if score > 0]
142
+ if min_score > 0:
143
+ filtered = [(idx, score) for idx, score in candidates if score >= min_score]
144
+ if len(candidates) != len(filtered):
145
+ print(f"[DEBUG] BM25: filtered {len(candidates) - len(filtered)} low-score results (threshold={min_score})", file=sys.stderr)
146
+ candidates = filtered
147
+
139
148
  results = []
140
- for idx, score in ranked[:max_results]:
141
- if score > 0:
142
- row = data[idx]
143
- results.append({col: row.get(col, "") for col in output_cols if col in row})
149
+ for idx, score in candidates[:max_results]:
150
+ row = data[idx]
151
+ entry = {col: row.get(col, "") for col in output_cols if col in row}
152
+ entry['_score'] = score
153
+ results.append(entry)
144
154
 
145
155
  return results
146
156
 
@@ -166,7 +176,7 @@ def detect_domain(query):
166
176
  return best if scores[best] > 0 else "components"
167
177
 
168
178
 
169
- def search(query, domain=None, max_results=MAX_RESULTS):
179
+ def search(query, domain=None, max_results=MAX_RESULTS, min_score=0.0):
170
180
  """Main search function with auto-domain detection"""
171
181
  if domain is None:
172
182
  domain = detect_domain(query)
@@ -177,7 +187,7 @@ def search(query, domain=None, max_results=MAX_RESULTS):
177
187
  if not filepath.exists():
178
188
  return {"error": f"File not found: {filepath}", "domain": domain}
179
189
 
180
- results = _search_csv(filepath, config["search_cols"], config["output_cols"], query, max_results)
190
+ results = _search_csv(filepath, config["search_cols"], config["output_cols"], query, max_results, min_score=min_score)
181
191
 
182
192
  return {
183
193
  "domain": domain,
@@ -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}/{passed + failed} checks passed\n\n"
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
 
@@ -15,7 +15,7 @@ def search_block_patterns(description):
15
15
  try:
16
16
  result = search(description, domain="block-patterns", max_results=5)
17
17
  if "error" in result or not result.get("results"):
18
- return "N/A"
18
+ return ""
19
19
  lines = ["**Relevant Block Patterns:**"]
20
20
  for r in result["results"]:
21
21
  lines.append(f"- [{r.get('Category', '')} / {r.get('Block Type', '')}] {r.get('Use Case', '')}")
@@ -25,7 +25,7 @@ def search_block_patterns(description):
25
25
  lines.append(f" Example: `{ex[:200]}`")
26
26
  return "\n".join(lines)
27
27
  except (FileNotFoundError, KeyError):
28
- return "N/A"
28
+ return ""
29
29
 
30
30
 
31
31
  def search_settings_profile(section_name):
@@ -33,7 +33,7 @@ def search_settings_profile(section_name):
33
33
  try:
34
34
  result = search(section_name, domain="settings-profiles", max_results=8)
35
35
  if "error" in result or not result.get("results"):
36
- return "N/A"
36
+ return ""
37
37
  lines = ["**Recommended Settings Profile:**",
38
38
  "| Setting ID | Type | Label | Default | Required |",
39
39
  "|------------|------|-------|---------|----------|"]
@@ -42,7 +42,7 @@ def search_settings_profile(section_name):
42
42
  f"{r.get('Label', '')} | {r.get('Default', '')} | {r.get('Required', '')} |")
43
43
  return "\n".join(lines)
44
44
  except (FileNotFoundError, KeyError):
45
- return "N/A"
45
+ return ""
46
46
 
47
47
 
48
48
  def search_theme_dna(theme_name):
@@ -50,11 +50,25 @@ def search_theme_dna(theme_name):
50
50
  try:
51
51
  result = search(theme_name, domain="themes", max_results=1)
52
52
  if "error" in result or not result.get("results"):
53
- return "N/A"
53
+ return ""
54
54
  r = result["results"][0]
55
55
  return (f"**Theme DNA:** {r.get('Theme', '')} — "
56
56
  f"CSS Prefix: {r.get('CSS Variable Prefix', '')} | "
57
57
  f"Naming: {r.get('Section Naming', '')} | "
58
58
  f"Blocks: {r.get('Block Conventions', '')}")
59
59
  except (FileNotFoundError, KeyError):
60
- return "N/A"
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
@@ -25,6 +26,7 @@ _hs.loader.exec_module(_hm)
25
26
  search_block_patterns = _hm.search_block_patterns
26
27
  search_settings_profile = _hm.search_settings_profile
27
28
  search_theme_dna = _hm.search_theme_dna
29
+ search_design_tokens = _hm.search_design_tokens
28
30
 
29
31
  TEMPLATE_DIR = Path(__file__).parent.parent / "templates"
30
32
 
@@ -48,8 +50,8 @@ def load_template(name):
48
50
  return ""
49
51
 
50
52
 
51
- def search_components(description, max_results=2):
52
- """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"""
53
55
  result = search_component(description, max_results=max_results)
54
56
  if "error" in result or not result.get("results"):
55
57
  return "No matching component patterns found."
@@ -59,14 +61,14 @@ def search_components(description, max_results=2):
59
61
  output.append(f"**{r.get('Name', 'Unknown')}** ({r.get('Category', '')}, {r.get('Difficulty', '')})")
60
62
  output.append(f"Blocks: {r.get('Blocks Used', '')} | Settings: {r.get('Settings Count', '')}")
61
63
  if 'liquid_content' in r:
62
- output.append(f"```liquid\n{r['liquid_content'][:3500]}\n```")
64
+ output.append(f"```liquid\n{r['liquid_content'][:6000]}\n```")
63
65
  output.append("")
64
66
  return "\n".join(output)
65
67
 
66
68
 
67
69
  def search_schema_settings(description, max_results=5):
68
70
  """Search schema-library for relevant setting types"""
69
- result = search(description, domain="schema", max_results=max_results)
71
+ result = search(description, domain="schema", max_results=max_results, min_score=0.3)
70
72
  if "error" in result or not result.get("results"):
71
73
  return "No matching schema settings found."
72
74
 
@@ -81,7 +83,7 @@ def search_schema_settings(description, max_results=5):
81
83
 
82
84
  def search_best_practices(description, max_results=5):
83
85
  """Search best practices for applicable rules"""
84
- result = search(description, domain="practices", max_results=max_results)
86
+ result = search(description, domain="practices", max_results=max_results, min_score=0.3)
85
87
  if "error" in result or not result.get("results"):
86
88
  return "No matching best practices found."
87
89
 
@@ -98,7 +100,7 @@ def search_best_practices(description, max_results=5):
98
100
 
99
101
 
100
102
  def try_ui_ux_bridge(ui_style_keywords):
101
- """Optional: query ui-ux-pro-max if available"""
103
+ """Optional: query ui-ux-pro-max if available. Returns empty string if N/A."""
102
104
  try:
103
105
  ux_search_path = Path(__file__).parent.parent.parent.parent / ".claude" / "skills" / "ui-ux-pro-max" / "scripts" / "core.py"
104
106
  if not ux_search_path.exists():
@@ -111,19 +113,27 @@ def try_ui_ux_bridge(ui_style_keywords):
111
113
  spec.loader.exec_module(ux_module)
112
114
  result = ux_module.search(ui_style_keywords, domain="style", max_results=2)
113
115
  if result.get("results"):
114
- lines = ["**UI/UX Style Recommendations (from ui-ux-pro-max):**"]
116
+ lines = ["<design_recommendations>", "**UI/UX Style Recommendations:**"]
115
117
  for r in result["results"]:
116
118
  lines.append(f"- {r.get('Style Category', '')}: {r.get('Keywords', '')}")
117
119
  lines.append(f" Effects: {r.get('Effects & Animation', '')}")
120
+ lines.append("</design_recommendations>")
118
121
  return "\n".join(lines)
119
122
  except Exception:
120
123
  pass
121
- return "N/A ui-ux-pro-max not available"
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)
122
131
 
123
132
 
124
133
  def assemble_context(name, description, theme_profile, component, schema,
125
- practices, design_recs, block_patterns="N/A",
126
- settings_profile="N/A", theme_dna="N/A"):
134
+ practices, design_recs, block_patterns="",
135
+ settings_profile="", theme_dna="",
136
+ design_tokens="", refinement=""):
127
137
  """Merge all context into generation prompt"""
128
138
  template = load_template("generation-prompt.md")
129
139
  base_template = load_template("section-base.liquid")
@@ -140,44 +150,16 @@ def assemble_context(name, description, theme_profile, component, schema,
140
150
  content = content.replace("{{ block_patterns_results }}", block_patterns)
141
151
  content = content.replace("{{ settings_profile_results }}", settings_profile)
142
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)
143
155
  base_block = f"```liquid\n{base_template}\n```" if base_template else "See standard OS2.0 section structure"
144
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)
145
159
  return content
146
160
 
147
- # Fallback: manual assembly
148
- return f"""# Generate Shopify Section: {name}
149
-
150
- ## Description
151
- {description}
152
-
153
- ## Theme Context
154
- {theme_profile}
155
-
156
- ## Closest Pattern Template
157
- {component}
158
-
159
- ## Relevant Schema Settings
160
- {schema}
161
-
162
- ## Applicable Best Practices
163
- {practices}
164
-
165
- ## Design Recommendations
166
- {design_recs}
167
-
168
- ## Base Template
169
- ```liquid
170
- {base_template}
171
- ```
172
-
173
- ## Requirements
174
- - OS2.0 compliant (presets, @app blocks, block.shopify_attributes)
175
- - Scoped CSS via #section-{{{{ section.id }}}}
176
- - Responsive (mobile-first)
177
- - Accessible (semantic HTML, aria-labels, alt text)
178
- - Performance (lazy-load non-hero images, no Liquid in CSS/JS)
179
- - Use {{% render %}} not {{% include %}}
180
- """
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}"
181
163
 
182
164
 
183
165
  def main():
@@ -189,14 +171,18 @@ def main():
189
171
  args = parser.parse_args()
190
172
 
191
173
  theme = load_theme_profile(args.theme_profile)
174
+ # Tailored BM25 queries per domain for better relevance
192
175
  component = search_components(f"{args.name} {args.description}")
193
- schema = search_schema_settings(args.description)
194
- practices = search_best_practices(args.description)
176
+ schema = search_schema_settings(f"shopify section settings {args.description}")
177
+ practices = search_best_practices(f"shopify section {args.name} best practices")
195
178
  design_recs = try_ui_ux_bridge(args.name + " " + args.description)
196
179
  blocks = search_block_patterns(f"{args.name} {args.description}")
197
180
  profile = search_settings_profile(args.name)
198
181
  dna = search_theme_dna(args.name)
182
+ tokens = search_design_tokens(f"css typography spacing color {args.description}")
199
183
 
184
+ # Build refinement context as dedicated section (not appended to component)
185
+ refinement = ""
200
186
  if args.refine:
201
187
  refine_path = Path(args.refine)
202
188
  try:
@@ -207,13 +193,18 @@ def main():
207
193
  try:
208
194
  from quality_gate import run_quality_gate
209
195
  gate_result = run_quality_gate(args.refine)
210
- refine_context = f"\n## Existing Section (for refinement)\n```liquid\n{existing[:3000]}\n```\n\n## Quality Gate Results\n{gate_result}\n\nPlease fix the issues identified above while preserving the section's functionality."
211
- component += refine_context
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>")
212
201
  except ImportError:
213
- component += f"\n## Existing Section (for refinement)\n```liquid\n{existing[:3000]}\n```"
202
+ refinement = (f"<refinement>\n## Existing Section (for refinement)\n"
203
+ f"```liquid\n{existing[:3000]}\n```\n</refinement>")
214
204
 
215
205
  context = assemble_context(args.name, args.description, theme, component, schema,
216
- practices, design_recs, blocks, profile, dna)
206
+ practices, design_recs, blocks, profile, dna,
207
+ design_tokens=tokens, refinement=refinement)
217
208
  print(context)
218
209
 
219
210
 
@@ -1,41 +1,96 @@
1
- # Generate Shopify Section: {{ name }}
1
+ <role>
2
+ You are an expert Shopify OS2.0 theme developer. Generate production-ready section .liquid files that follow Shopify best practices, are fully accessible, responsive, and theme-editor friendly.
3
+ </role>
2
4
 
3
- ## Description
4
- {{ description }}
5
-
6
- ## Theme Context
7
- {{ theme_profile_summary }}
8
-
9
- ## Closest Pattern Template
5
+ <reference_components>
10
6
  {{ component_pattern_content }}
7
+ </reference_components>
11
8
 
12
- ## Relevant Schema Settings
9
+ <schema_types>
13
10
  {{ schema_search_results }}
11
+ </schema_types>
14
12
 
15
- ## Recommended Settings Profile
16
- {{ settings_profile_results }}
17
-
18
- ## Block Pattern References
13
+ <block_patterns>
19
14
  {{ block_patterns_results }}
15
+ </block_patterns>
20
16
 
21
- ## Theme DNA
22
- {{ theme_dna_context }}
17
+ <settings_profile>
18
+ {{ settings_profile_results }}
19
+ </settings_profile>
23
20
 
24
- ## Applicable Best Practices
21
+ <design_tokens>
22
+ {{ design_tokens_results }}
23
+ </design_tokens>
24
+
25
+ <best_practices>
25
26
  {{ best_practices_results }}
27
+ </best_practices>
28
+
29
+ <theme_context>
30
+ {{ theme_profile_summary }}
31
+ </theme_context>
32
+
33
+ <theme_dna>
34
+ {{ theme_dna_context }}
35
+ </theme_dna>
26
36
 
27
- ## Design Recommendations
28
37
  {{ ui_ux_recommendations }}
29
38
 
30
- ## Base Template
39
+ <task>
40
+ <name>{{ name }}</name>
41
+ <description>{{ description }}</description>
42
+ </task>
43
+
44
+ <base_template>
31
45
  {{ section_base_content }}
46
+ </base_template>
32
47
 
33
- ## Requirements
48
+ {{ refinement_context }}
49
+
50
+ <mandatory_settings>
51
+ CONDITIONAL RULES (apply based on section content):
52
+ - IF section has heading or subheading text settings:
53
+ -> MUST include font_picker for primary heading
54
+ -> MUST include heading_tag (type: select, options: h1/h2/h3/h4) for SEO
55
+ - ALWAYS include:
56
+ -> color_scheme (type: color_scheme) for theme-wide color customization
57
+ -> padding_top + padding_bottom (type: range, 0-100px, step 4)
58
+ -> All colors via CSS custom properties, NEVER hardcoded hex
59
+ - IF section has CTA button:
60
+ -> Button settings: button_label, button_link, button_style
61
+ </mandatory_settings>
62
+
63
+ <pre_output_checklist>
64
+ Before writing code, verify your plan includes ALL:
65
+ - color_scheme setting in schema
66
+ - font_picker for heading text (if section has headings)
67
+ - heading_tag select (h1/h2/h3/h4) (if section has headings)
68
+ - Padding/margin controls
69
+ - presets array with at least one preset
70
+ - @app block type
71
+ - CSS: color + font-size set directly on h1-h6, p, a selectors (not just parent containers)
72
+ - CSS: scoped via #section-{{ section.id }}
73
+ - Images: loading="lazy" except hero; hero uses fetchpriority="high"
74
+ - All block wrappers include {{ block.shopify_attributes }}
75
+ - No hardcoded hex colors in CSS
76
+ - Uses {% render %} not {% include %}
77
+ </pre_output_checklist>
78
+
79
+ <requirements>
34
80
  - OS2.0 compliant (presets, @app blocks, block.shopify_attributes)
35
81
  - Scoped CSS via #section-{{ section.id }}
36
- - Text element specificity: ALWAYS set `color` and `font-size` directly on text element selectors (h1-h6, p, a), not just parent containers. Shopify themes apply color directly to these elements via global CSS which overrides inheritance. Use `#section-{{ section.id }} .my-heading { color: var(--text-color); font-size: clamp(...); }` not just `.my-wrapper { color: var(--text-color); }`
82
+ - Text element specificity: ALWAYS set color and font-size directly on text element selectors (h1-h6, p, a), not just parent containers
37
83
  - Responsive (mobile-first)
38
84
  - Accessible (semantic HTML, aria-labels, alt text)
39
85
  - Performance (lazy-load non-hero images, no Liquid in CSS/JS)
40
86
  - Use {% render %} not {% include %}
41
87
  - Include {{ block.shopify_attributes }} on ALL block wrappers
88
+ </requirements>
89
+
90
+ <output_format>
91
+ Output ONLY the raw .liquid file content.
92
+ Do not wrap in markdown code blocks.
93
+ Do not add explanations before or after.
94
+ Begin with the first line of the liquid file.
95
+ End with the {% endschema %} tag.
96
+ </output_format>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rebly-sections",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Shopify section AI coding skill installer for Claude Code and Antigravity Kit",
5
5
  "author": "Rebly Sections",
6
6
  "homepage": "https://github.com/rebly-sections/sections-ai#readme",