📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate agent guide claims and run its locked semantic examples."""
|
||||
|
||||
import csv
|
||||
import json
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DATA = ROOT / "src/ui-ux-pro-max/data"
|
||||
TEMPLATES = ROOT / "src/ui-ux-pro-max/templates"
|
||||
PLATFORMS = TEMPLATES / "platforms"
|
||||
GUIDE = TEMPLATES / "base/skill-content.md"
|
||||
CLAUDE_GUIDE = ROOT / ".claude/skills/ui-ux-pro-max/SKILL.md"
|
||||
README = ROOT / "README.md"
|
||||
README_ZH = ROOT / "README.zh.md"
|
||||
DOMAINS = {
|
||||
"product", "style", "typography", "color", "landing", "chart", "ux",
|
||||
"gsap", "react", "web", "icons", "google-fonts",
|
||||
}
|
||||
FLAGS = {
|
||||
"--domain", "-d", "--stack", "-s", "--max-results", "-n", "--json",
|
||||
"--full", "--design-system", "-ds", "--project-name", "-p", "--format",
|
||||
"-f", "--persist", "--page", "--output-dir", "-o", "--force",
|
||||
"--variance", "--motion", "--density",
|
||||
}
|
||||
PLATFORM_COUNT = 20
|
||||
GUIDE_REQUIREMENTS = (
|
||||
"## Query Contract", "one dominant intent", "Retry once",
|
||||
"Do not persist unverified output", "explicit accessibility outcome terms",
|
||||
)
|
||||
SEARCH_COMMAND = re.compile(r"search\.py|\{\{SCRIPT_PATH\}\}")
|
||||
QUOTED_QUERY = re.compile(r'search\.py"?\s+"[^"]+"')
|
||||
DOMAIN_FLAG = re.compile(r"--domain\s+(\S+)")
|
||||
STACK_FLAG = re.compile(r"--stack\s+(\S+)")
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def row_count(name):
|
||||
with (DATA / name).open(encoding="utf-8", newline="") as handle:
|
||||
return sum(1 for _ in csv.DictReader(handle))
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def style_counts():
|
||||
with (DATA / "styles.csv").open(encoding="utf-8", newline="") as handle:
|
||||
rows = list(csv.DictReader(handle))
|
||||
return {
|
||||
"searchable": sum(row.get("Status") != "deprecated" for row in rows),
|
||||
"active": sum(row.get("Status") == "active" for row in rows),
|
||||
}
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def stack_names():
|
||||
return frozenset(path.stem for path in (DATA / "stacks").glob("*.csv"))
|
||||
|
||||
|
||||
def expected_description():
|
||||
styles = style_counts()
|
||||
return (
|
||||
"UI/UX design intelligence for web, mobile, and desktop. This skill should be used when designing, "
|
||||
"building, reviewing, or fixing interfaces, including pages, components, design "
|
||||
"systems, accessibility, interaction, responsive layout, typography, color, "
|
||||
"charts, and stack-specific UI implementation. Searchable local data: "
|
||||
f"{styles['searchable']} searchable styles ({styles['active']} active), "
|
||||
f"{row_count('colors.csv')} product palettes "
|
||||
f"and reasoning profiles, {row_count('typography.csv')} font pairings, "
|
||||
f"{row_count('ux-guidelines.csv')} UX guidelines, {row_count('icons.csv')} icons, "
|
||||
f"{row_count('motion.csv')} GSAP presets, {row_count('charts.csv')} chart types, "
|
||||
f"and {len(stack_names())} stacks."
|
||||
)
|
||||
|
||||
|
||||
def run_json(*args):
|
||||
command = [
|
||||
sys.executable, str(ROOT / "src/ui-ux-pro-max/scripts/search.py"),
|
||||
*args, "--json",
|
||||
]
|
||||
completed = subprocess.run(command, check=True, capture_output=True, text=True)
|
||||
return json.loads(completed.stdout)
|
||||
|
||||
|
||||
def validate_commands(label, text):
|
||||
errors = []
|
||||
commands = []
|
||||
for line in text.splitlines():
|
||||
command = line.strip()
|
||||
if command.startswith(("python ", "python3 ")) and (
|
||||
SEARCH_COMMAND.search(command)):
|
||||
commands.append(command.replace("{{SCRIPT_PATH}}", "search.py"))
|
||||
if not commands:
|
||||
errors.append(f"{label}: no documented search commands found")
|
||||
for command in commands:
|
||||
if not QUOTED_QUERY.search(command):
|
||||
errors.append(f"{label}: query must immediately follow search.py: {command}")
|
||||
try:
|
||||
tokens = shlex.split(command.replace("[", "").replace("]", ""))
|
||||
except ValueError as exc:
|
||||
errors.append(f"{label}: invalid shell command ({exc}): {command}")
|
||||
continue
|
||||
unknown_flags = sorted({token for token in tokens if token.startswith("-") and token not in FLAGS})
|
||||
if unknown_flags:
|
||||
errors.append(f"{label}: unknown flags {unknown_flags}: {command}")
|
||||
domain = DOMAIN_FLAG.search(command)
|
||||
if domain and domain.group(1) not in DOMAINS | {"<domain>"}:
|
||||
errors.append(f"{label}: unknown domain {domain.group(1)!r}")
|
||||
stack = STACK_FLAG.search(command)
|
||||
if stack and stack.group(1) not in stack_names() | {"<stack>"}:
|
||||
errors.append(f"{label}: unknown stack {stack.group(1)!r}")
|
||||
if "--persist" in command and "--output-dir" not in command:
|
||||
errors.append(f"{label}: persisted command lacks --output-dir")
|
||||
return errors
|
||||
|
||||
|
||||
def validate():
|
||||
errors = []
|
||||
product_counts = {
|
||||
"products": row_count("products.csv"),
|
||||
"palettes": row_count("colors.csv"),
|
||||
"reasoning profiles": row_count("ui-reasoning.csv"),
|
||||
}
|
||||
if len(set(product_counts.values())) != 1:
|
||||
errors.append(f"product/palette/reasoning counts differ: {product_counts}")
|
||||
description = expected_description()
|
||||
claude_text = CLAUDE_GUIDE.read_text(encoding="utf-8")
|
||||
if description not in claude_text:
|
||||
errors.append("claude: frontmatter description/counts differ from canonical metadata")
|
||||
styles = style_counts()
|
||||
guide_claims = (
|
||||
f"{styles['searchable']} searchable styles ({styles['active']} active)",
|
||||
f"{row_count('typography.csv')} font pairings",
|
||||
f"{row_count('ux-guidelines.csv')} UX guidelines",
|
||||
f"{row_count('icons.csv')} curated icons",
|
||||
f"{row_count('motion.csv')} GSAP presets",
|
||||
f"{row_count('charts.csv')} chart types",
|
||||
f"{len(stack_names())} technology stacks",
|
||||
)
|
||||
for claim in guide_claims:
|
||||
if claim not in claude_text:
|
||||
errors.append(f"claude: missing live count claim {claim!r}")
|
||||
reasoning_count = row_count("ui-reasoning.csv")
|
||||
public_claims = (
|
||||
(README, (f"{reasoning_count} Reasoning Rules",
|
||||
f"{row_count('ux-guidelines.csv')} UX Guidelines")),
|
||||
(README_ZH, (f"{reasoning_count} 条推理规则",
|
||||
f"{row_count('ux-guidelines.csv')} 条 UX 指南")),
|
||||
)
|
||||
for path, claims in public_claims:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for claim in claims:
|
||||
if claim not in text:
|
||||
errors.append(f"{path.name}: missing live count claim {claim!r}")
|
||||
configs = sorted(PLATFORMS.glob("*.json"))
|
||||
if len(configs) != PLATFORM_COUNT:
|
||||
errors.append(f"expected {PLATFORM_COUNT} platform configs, got {len(configs)}")
|
||||
for path in configs:
|
||||
config = json.loads(path.read_text(encoding="utf-8"))
|
||||
if config.get("description") != description:
|
||||
errors.append(f"{path.name}: stale description")
|
||||
frontmatter = config.get("frontmatter") or {}
|
||||
if frontmatter.get("description") and frontmatter["description"] != description:
|
||||
errors.append(f"{path.name}: stale frontmatter description")
|
||||
for label, path in (("canonical", GUIDE), ("claude", CLAUDE_GUIDE)):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for phrase in GUIDE_REQUIREMENTS:
|
||||
if phrase not in text:
|
||||
errors.append(f"{label}: missing {phrase!r}")
|
||||
errors.extend(validate_commands(label, text))
|
||||
design = run_json("beauty spa wellness service", "--design-system")
|
||||
if design["design_system"]["category"] != "Beauty/Spa/Wellness Service":
|
||||
errors.append("design-system example resolved wrong product category")
|
||||
ux = run_json("keyboard focus modal", "--domain", "ux")
|
||||
if ux.get("domain") != "ux" or not ux.get("results"):
|
||||
errors.append("focused UX example did not return an explicit UX match")
|
||||
stack = run_json("virtualized list", "--stack", "react-native")
|
||||
if stack.get("stack") != "react-native" or not stack.get("results"):
|
||||
errors.append("React Native stack example did not return a stack match")
|
||||
return errors
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
problems = validate()
|
||||
if problems:
|
||||
print("Agent guide validation failed:\n- " + "\n- ".join(problems), file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
print(
|
||||
f"Agent guide validation passed: {PLATFORM_COUNT} platforms "
|
||||
"and 3 locked examples checked."
|
||||
)
|
||||
Reference in New Issue
Block a user