mirror of
https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git
synced 2026-08-01 19:06:14 +00:00
* fix(cli): install all 7 skills via uipro init, not just the orchestrator `uipro init` rendered only the orchestrator (ui-ux-pro-max) and never delivered the 6 sibling skills (banner-design, brand, design, design-system, slides, ui-styling), so users got 1 of 7 skills (#362). - sync-assets.mjs: bundle the 6 sub-skills into cli/assets/skills/ as static copies (source of truth: .claude/skills/), with sync + check coverage. Excludes ui-styling/canvas-fonts (~5.8MB of TTF) and __pycache__/.pyc cruft — a skill registers from its SKILL.md, not its fonts — so the bundle adds ~0.9MB, not ~6.6MB. - template.ts: after rendering the orchestrator, install each bundled sub-skill as a sibling. The skills parent is derived from the platform's skillPath (skills/ for most, prompts/ for copilot, steering/ for kiro) rather than hardcoded. - uninstall.ts: remove the sub-skills too. Verified: check:assets in sync, tsc passes, and a per-platform install harness delivers all 7 skills to the correct parent dir with no fonts. Closes #362 * fix(cli): filter excluded files from target side of check:assets check:assets filtered sourceFiles with isExcludedAssetFile but not targetFiles, so a stray cli/assets/scripts/__pycache__/*.pyc (generated by a local Python run) was reported as an "extra asset file" and failed the gate. Apply the same predicate to targetFiles in both the dirsToSync and sub-skill loops. Verified: check:assets now passes with __pycache__/*.pyc present in the target tree; typecheck passes. * fix(cli): uninstall from each platform's real skills dir, not hardcoded skills/ removeSkillDir() hardcoded <folder>/skills/<name>, but the installer places skills under each platform config's skillPath parent — copilot in .github/prompts/, kiro in .kiro/steering/. So uninstall left those platforms' skills (orchestrator + sub-skills) behind. Derive the install parent from loadPlatformConfig(aiType).folderStructure (same source the installer uses), and keep the legacy <folder>/skills/ cleanup (incl. .shared/) for older installs. Deduped via a Set. Verified: typecheck passes; an install+uninstall harness removes all 7 skills with zero leftovers for claude (.claude/skills), copilot (.github/prompts) and kiro (.kiro/steering). * fix(cli): re-sync bundled sub-skills after #385 stripped ckm- names #385 merged to main and removed the ckm- prefix from the six .claude/skills/*/SKILL.md name fields. This branch's bundled copies under cli/assets/skills/ still carried the old ckm- names, so after the PR merges with main the source no longer matched the bundle and the check-asset-sync CI gate failed (stale asset file: skills/*/SKILL.md). Merge main and regenerate the bundle so cli/assets/skills matches the current .claude/skills source of truth. check:assets and typecheck pass.
128 lines
4.4 KiB
Python
128 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
CIP Design Search CLI - Search corporate identity design guidelines
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add parent directory for imports
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
from core import search, search_all, get_cip_brief, CSV_CONFIG
|
|
|
|
|
|
def format_results(results, domain):
|
|
"""Format search results for display"""
|
|
if not results:
|
|
return "No results found."
|
|
|
|
output = []
|
|
for i, item in enumerate(results, 1):
|
|
output.append(f"\n{'='*60}")
|
|
output.append(f"Result {i}:")
|
|
for key, value in item.items():
|
|
if value:
|
|
output.append(f" {key}: {value}")
|
|
return "\n".join(output)
|
|
|
|
|
|
def format_brief(brief):
|
|
"""Format CIP brief for display"""
|
|
output = []
|
|
output.append(f"\n{'='*60}")
|
|
output.append(f"CIP DESIGN BRIEF: {brief['brand_name']}")
|
|
output.append(f"{'='*60}")
|
|
|
|
if brief.get("industry"):
|
|
output.append(f"\n📊 INDUSTRY: {brief['industry'].get('Industry', 'N/A')}")
|
|
output.append(f" Style: {brief['industry'].get('CIP Style', 'N/A')}")
|
|
output.append(f" Mood: {brief['industry'].get('Mood', 'N/A')}")
|
|
|
|
if brief.get("style"):
|
|
output.append(f"\n🎨 DESIGN STYLE: {brief['style'].get('Style Name', 'N/A')}")
|
|
output.append(f" Description: {brief['style'].get('Description', 'N/A')}")
|
|
output.append(f" Materials: {brief['style'].get('Materials', 'N/A')}")
|
|
output.append(f" Finishes: {brief['style'].get('Finishes', 'N/A')}")
|
|
|
|
if brief.get("color_system"):
|
|
output.append(f"\n🎯 COLOR SYSTEM:")
|
|
output.append(f" Primary: {brief['color_system'].get('primary', 'N/A')}")
|
|
output.append(f" Secondary: {brief['color_system'].get('secondary', 'N/A')}")
|
|
|
|
output.append(f"\n✏️ TYPOGRAPHY: {brief.get('typography', 'N/A')}")
|
|
|
|
if brief.get("recommended_deliverables"):
|
|
output.append(f"\n📦 RECOMMENDED DELIVERABLES:")
|
|
for d in brief["recommended_deliverables"]:
|
|
output.append(f" • {d.get('Deliverable', 'N/A')}: {d.get('Description', '')[:60]}...")
|
|
|
|
return "\n".join(output)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Search CIP design guidelines",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
# Search deliverables
|
|
python search.py "business card"
|
|
|
|
# Search specific domain
|
|
python search.py "luxury elegant" --domain style
|
|
|
|
# Generate CIP brief
|
|
python search.py "tech startup" --cip-brief -b "TechFlow"
|
|
|
|
# Search all domains
|
|
python search.py "corporate professional" --all
|
|
|
|
# JSON output
|
|
python search.py "vehicle branding" --json
|
|
"""
|
|
)
|
|
|
|
parser.add_argument("query", help="Search query")
|
|
parser.add_argument("--domain", "-d", choices=list(CSV_CONFIG.keys()),
|
|
help="Search domain (auto-detected if not specified)")
|
|
parser.add_argument("--max", "-m", type=int, default=3, help="Max results (default: 3)")
|
|
parser.add_argument("--all", "-a", action="store_true", help="Search all domains")
|
|
parser.add_argument("--cip-brief", "-c", action="store_true", help="Generate CIP brief")
|
|
parser.add_argument("--brand", "-b", default="BrandName", help="Brand name for CIP brief")
|
|
parser.add_argument("--style", "-s", help="Style override for CIP brief")
|
|
parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.cip_brief:
|
|
brief = get_cip_brief(args.brand, args.query, args.style)
|
|
if args.json:
|
|
print(json.dumps(brief, indent=2))
|
|
else:
|
|
print(format_brief(brief))
|
|
elif args.all:
|
|
results = search_all(args.query, args.max)
|
|
if args.json:
|
|
print(json.dumps(results, indent=2))
|
|
else:
|
|
for domain, items in results.items():
|
|
print(f"\n{'#'*60}")
|
|
print(f"# {domain.upper()}")
|
|
print(format_results(items, domain))
|
|
else:
|
|
result = search(args.query, args.domain, args.max)
|
|
if args.json:
|
|
print(json.dumps(result, indent=2))
|
|
else:
|
|
print(f"\nDomain: {result['domain']}")
|
|
print(f"Query: {result['query']}")
|
|
print(f"Results: {result['count']}")
|
|
print(format_results(result.get("results", []), result["domain"]))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|