From 3294e63c9fd59ac09920df3592e14e69d78fac4c Mon Sep 17 00:00:00 2001 From: "ci[bot]" Date: Thu, 3 Sep 2026 08:57:39 +0000 Subject: [PATCH] :package: deps(thirdparty): update snapshots --- brooks-lint/CLAUDE.md | 2 +- brooks-lint/README.es.md | 11 +- brooks-lint/README.ja.md | 11 +- brooks-lint/README.ko.md | 11 +- brooks-lint/README.md | 11 +- brooks-lint/README.zh-CN.md | 11 +- brooks-lint/README.zh-TW.md | 11 +- brooks-lint/SOURCE.md | 4 +- brooks-lint/assets/star-history.json | 29 +- brooks-lint/assets/star-history.svg | 20 +- brooks-lint/docs/bob-setup.md | 53 ++ brooks-lint/docs/getting-started.md | 5 +- brooks-lint/scripts/install.sh | 6 +- brooks-lint/scripts/platforms.mjs | 23 + brooks-lint/scripts/validate-repo.mjs | 30 +- brooks-lint/scripts/validate-repo.test.mjs | 41 +- .../.claude/skills/banner-design/SKILL.md | 105 +-- ui-ux-pro-max/.claude/skills/brand/SKILL.md | 4 + .../brand/references/approval-checklist.md | 2 +- .../.claude/skills/brand/references/update.md | 4 +- .../skills/brand/scripts/extract-colors.cjs | 6 +- .../brand/scripts/sync-brand-to-tokens.cjs | 9 +- .../tests/test_sync_brand_to_tokens.py | 8 + .../.claude/skills/design-system/SKILL.md | 4 + .../design-system/scripts/embed-tokens.cjs | 8 +- .../design-system/scripts/fetch-background.py | 26 +- .../scripts/html-token-validator.py | 36 +- .../design-system/scripts/search-slides.py | 10 + .../scripts/tests/test_validate_tokens.py | 6 +- ui-ux-pro-max/.claude/skills/design/SKILL.md | 67 +- .../skills/design/references/cip-design.md | 24 +- .../design/references/design-routing.md | 8 +- .../skills/design/references/icon-design.md | 16 +- .../skills/design/references/logo-design.md | 34 +- .../references/slides-copywriting-formulas.md | 4 +- .../references/slides-layout-patterns.md | 4 +- .../design/references/slides-strategies.md | 4 +- .../skills/design/scripts/cip/generate.py | 5 +- .../skills/design/scripts/logo/generate.py | 212 +++++- .../scripts/logo/tests/test_generate.py | 156 +++++ ui-ux-pro-max/.claude/skills/slides/SKILL.md | 4 + .../slides/references/copywriting-formulas.md | 4 +- .../slides/references/layout-patterns.md | 4 +- .../slides/references/slide-strategies.md | 4 +- .../.claude/skills/ui-styling/SKILL.md | 4 + .../ui-ux-pro-max/data/catalog-summary.json | 2 +- .../test_catalog_summary_line_endings.py | 78 +++ .../scripts/tests/test_skill_script_paths.py | 82 +++ .../ui-ux-pro-max/scripts/validate_data.py | 6 +- ui-ux-pro-max/.gitattributes | 6 + .../.github/workflows/check-asset-sync.yml | 63 +- ui-ux-pro-max/README.id.md | 663 ++++++++++++++++++ ui-ux-pro-max/README.ko.md | 632 +++++++++++++++++ ui-ux-pro-max/README.md | 21 +- ui-ux-pro-max/README.vi.md | 19 +- ui-ux-pro-max/README.zh.md | 19 +- ui-ux-pro-max/SOURCE.md | 4 +- .../cli/assets/data/catalog-summary.json | 2 +- .../test_catalog_summary_line_endings.py | 78 +++ .../scripts/tests/test_skill_script_paths.py | 82 +++ .../cli/assets/scripts/validate_data.py | 6 +- .../cli/assets/skills/banner-design/SKILL.md | 105 +-- .../cli/assets/skills/brand/SKILL.md | 4 + .../brand/references/approval-checklist.md | 2 +- .../assets/skills/brand/references/update.md | 4 +- .../skills/brand/scripts/extract-colors.cjs | 6 +- .../brand/scripts/sync-brand-to-tokens.cjs | 9 +- .../tests/test_sync_brand_to_tokens.py | 8 + .../cli/assets/skills/design-system/SKILL.md | 4 + .../design-system/scripts/embed-tokens.cjs | 8 +- .../design-system/scripts/fetch-background.py | 26 +- .../scripts/html-token-validator.py | 36 +- .../design-system/scripts/search-slides.py | 10 + .../scripts/tests/test_validate_tokens.py | 6 +- .../cli/assets/skills/design/SKILL.md | 67 +- .../skills/design/references/cip-design.md | 24 +- .../design/references/design-routing.md | 8 +- .../skills/design/references/icon-design.md | 16 +- .../skills/design/references/logo-design.md | 34 +- .../references/slides-copywriting-formulas.md | 4 +- .../references/slides-layout-patterns.md | 4 +- .../design/references/slides-strategies.md | 4 +- .../skills/design/scripts/cip/generate.py | 5 +- .../skills/design/scripts/logo/generate.py | 212 +++++- .../scripts/logo/tests/test_generate.py | 156 +++++ .../cli/assets/skills/slides/SKILL.md | 4 + .../slides/references/copywriting-formulas.md | 4 +- .../slides/references/layout-patterns.md | 4 +- .../slides/references/slide-strategies.md | 4 +- .../cli/assets/skills/ui-styling/SKILL.md | 4 + ui-ux-pro-max/cli/package.json | 16 +- ui-ux-pro-max/cli/scripts/run-python.mjs | 18 + .../e2e/banner-design-path-contract.spec.ts | 66 ++ .../scripts/generate-catalog-summary.py | 4 +- .../ui-ux-pro-max/data/catalog-summary.json | 2 +- .../test_catalog_summary_line_endings.py | 78 +++ .../scripts/tests/test_skill_script_paths.py | 82 +++ .../ui-ux-pro-max/scripts/validate_data.py | 6 +- 98 files changed, 3411 insertions(+), 467 deletions(-) create mode 100644 brooks-lint/docs/bob-setup.md create mode 100644 ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/tests/test_catalog_summary_line_endings.py create mode 100644 ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py create mode 100644 ui-ux-pro-max/.gitattributes create mode 100644 ui-ux-pro-max/README.id.md create mode 100644 ui-ux-pro-max/README.ko.md create mode 100644 ui-ux-pro-max/cli/assets/scripts/tests/test_catalog_summary_line_endings.py create mode 100644 ui-ux-pro-max/cli/assets/scripts/tests/test_skill_script_paths.py create mode 100644 ui-ux-pro-max/cli/scripts/run-python.mjs create mode 100644 ui-ux-pro-max/cli/tests/e2e/banner-design-path-contract.spec.ts create mode 100644 ui-ux-pro-max/src/ui-ux-pro-max/scripts/tests/test_catalog_summary_line_endings.py create mode 100644 ui-ux-pro-max/src/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py diff --git a/brooks-lint/CLAUDE.md b/brooks-lint/CLAUDE.md index 6004f1e0..ff6c60da 100644 --- a/brooks-lint/CLAUDE.md +++ b/brooks-lint/CLAUDE.md @@ -36,7 +36,7 @@ Guidance for Claude Code when modifying this repository. For repo layout, instal - `` field `commands` is not accepted `` — `"commands": []` is what stops the CLI from migrating `commands/*.md` into duplicate `source-command-brooks-*` skills (issue #22). Confirmed by installing both ways under an isolated `CODEX_HOME`: drop the field and Codex regenerates `.codex-plugin/migrated-command-skills/`. The CLI's `RawPluginManifest` accepts `commands`; only the ingestion schema rejects it. - `` skill `_shared` is missing `SKILL.md` `` — the validator only skips dot-prefixed dirs, and `_shared/` is a shared-framework dir by design (see above). Renaming it to `.shared/` would also require changing `install.sh`'s `cp -R "$SRC"/*`, whose glob does not match dot-dirs. - **GitHub Action cache:** `.github/actions/brooks-lint/action.yml` uses `actions/cache@v4` with built-in cache-hit guard — do NOT add a manual directory check. -- **Adding a platform is derived, not hardcoded:** create `docs/-setup.md`, add the platform to `install.sh`'s `PLATFORMS` **and** both `global_dir()` / `project_dir()` case tables, then link the new guide from every `README*.md` and `docs/getting-started.md`. `npm run validate` derives both sides (`scripts/platforms.mjs`) and fails on any gap — a guide missing from one translation, or a `PLATFORMS` entry with no directory mapping. Do not add a hand-maintained platform list anywhere. +- **Adding a platform is derived, not hardcoded:** create `docs/-setup.md`, add the platform to `install.sh`'s `PLATFORMS` **and** both `global_dir()` / `project_dir()` case tables, then link the new guide from every `README*.md` and `docs/getting-started.md` **and** add the name to each of those documents' ` = …` enumeration (the `<平台> = …` line in the zh translations). That enumeration must list every `PLATFORMS` entry, `claude` included — it is the one spot a platform has to be spelled out, since a bare mention elsewhere is worthless as a check (`~/.bob/skills` in a table row already contains "bob"). `npm run validate` derives all three sides (`scripts/platforms.mjs`) and fails on any gap — a guide missing from one translation, a `PLATFORMS` entry with no directory mapping, or a platform absent from one document's enumeration. Do not add a hand-maintained platform list anywhere. - **Star history is data-first, never hand-drawn:** `assets/star-history.json` (raw `starred_at` timestamps, no usernames) is the source of truth; `assets/star-history.svg` is a pure function of it. Never hand-edit the SVG — `npm run validate` re-renders and fails on any mismatch. `node scripts/gen-star-history.mjs` refetches (needs `GITHUB_TOKEN` or `gh auth`, since GitHub restricted the stargazers API to admins/collaborators on 2026-06-30); `--render-only` redraws offline from the committed data. The render must stay deterministic — anchoring the time axis to the clock would make the weekly workflow commit noise on every run. - **Custom risks:** Teams add project-specific risk codes via `custom-risks-guide.md` in their project root. Template lives at `skills/_shared/custom-risks-guide.md`. diff --git a/brooks-lint/README.es.md b/brooks-lint/README.es.md index 1c183477..19eff661 100644 --- a/brooks-lint/README.es.md +++ b/brooks-lint/README.es.md @@ -69,7 +69,7 @@ Para el mapeo completo de fuente a skill, incluyendo excepciones y protecciones /plugin marketplace add hyhmrright/brooks-lint /plugin install brooks-lint@brooks-lint-marketplace -# Cualquier otra plataforma de Agent Skills — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · … +# Cualquier otra plataforma de Agent Skills — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · Bob … curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- ``` @@ -78,7 +78,7 @@ Luego solo pide ("revisa este PR", "audita la arquitectura"), o ejecuta uno de l ([qué hace cada uno](#comandos-de-barra)). Cada hallazgo se devuelve como **Síntoma → Origen → Consecuencia → Remedio** con una cita de libro y -una puntuación de salud de 0 a 100. Las opciones completas de instalación (9 plataformas más) y la +una puntuación de salud de 0 a 100. Las opciones completas de instalación (10 plataformas más) y la configuración de CI/CD están [más abajo](#instalación). ## Los doce libros @@ -273,7 +273,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # pídelo dentro O usa el instalador de abajo: `./scripts/install.sh gemini` / `./scripts/install.sh codex`. -### Cualquier otra plataforma — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness +### Cualquier otra plataforma — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness · IBM Bob brooks-lint se distribuye como [Agent Skills](https://agentskills.io) estándar. **Cualquier agente que cargue Agent Skills ejecuta los seis modos sin conversión alguna** — un solo comando los instala: @@ -281,7 +281,7 @@ Skills ejecuta los seis modos sin conversión alguna** — un solo comando los i ```bash # elige tu plataforma; --project instala en el repositorio actual en lugar de en tu configuración global curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- -# = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents +# = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · claude · bob · agents ``` El instalador copia los skills **de forma plana** en la carpeta correcta, de modo que el framework compartido @@ -299,11 +299,12 @@ El instalador copia los skills **de forma plana** en la carpeta correcta, de mod | Kiro (AWS) | `~/.kiro/skills` | `AGENTS.md` | [configuración](docs/kiro-setup.md) | | Factory Droid | `~/.factory/skills` | `AGENTS.md` | [configuración](docs/factory-droid-setup.md) | | DeepSeek Harness (`dsh`) | `~/.dsh/skills` | `~/.agents/skills`, `AGENTS.md` | [configuración](docs/dsh-setup.md) | +| IBM Bob (`bob`) | `~/.bob/skills` | `AGENTS.md` | [configuración](docs/bob-setup.md) | Kiro, Factory Droid y DeepSeek Harness también registran `/brooks-review` automáticamente. ¿Nuevo en los skills, o usas un agente que no aparece aquí? Consulta **[docs/getting-started.md](docs/getting-started.md)**. -> **🧪 Estado de verificación.** Claude Code, Gemini CLI y Codex CLI están verificados por el mantenedor. Las nueve +> **🧪 Estado de verificación.** Claude Code, Gemini CLI y Codex CLI están verificados por el mantenedor. Las diez > plataformas anteriores están documentadas a partir de la especificación oficial de skills de cada herramienta y verificadas a nivel > de diseño de archivos (el instalador está probado), pero el mantenedor aún no las ha ejecutado de extremo a extremo en cada plataforma. ¿Probaste > alguna — funciona **o** está rota? [Abre un issue](https://github.com/hyhmrright/brooks-lint/issues/new) con diff --git a/brooks-lint/README.ja.md b/brooks-lint/README.ja.md index 7209819c..1883cdc3 100644 --- a/brooks-lint/README.ja.md +++ b/brooks-lint/README.ja.md @@ -69,7 +69,7 @@ /plugin marketplace add hyhmrright/brooks-lint /plugin install brooks-lint@brooks-lint-marketplace -# その他あらゆる Agent Skills プラットフォーム — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · … +# その他あらゆる Agent Skills プラットフォーム — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · Bob … curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- ``` @@ -78,7 +78,7 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts `/brooks-health`、`/brooks-sweep`([それぞれの機能](#スラッシュコマンド))。 すべての指摘は **症状 → 根源 → 結果 → 対策** の形式で、書籍の出典と 0〜100 の健全性スコアとともに -返されます。完全なインストール方法(さらに 9 つのプラットフォーム)と CI/CD のセットアップは +返されます。完全なインストール方法(さらに 10 のプラットフォーム)と CI/CD のセットアップは [以下](#インストール)を参照してください。 ## 十二冊の書籍 @@ -273,7 +273,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # Codex セッ または下記のインストーラーを使用:`./scripts/install.sh gemini` / `./scripts/install.sh codex`。 -### その他すべてのプラットフォーム — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness +### その他すべてのプラットフォーム — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness · IBM Bob brooks-lint は標準的な [Agent Skills](https://agentskills.io) として配布されています。**Agent Skills を読み込むエージェントなら、どれも変換なしで六つすべてのモードを実行できます**——1 つのコマンドでインストールできます: @@ -281,7 +281,7 @@ Skills を読み込むエージェントなら、どれも変換なしで六つ ```bash # プラットフォームを選択;--project はグローバル設定ではなく現在のリポジトリにインストール curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- -# = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents +# = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · claude · bob · agents ``` インストーラーはスキルをあなたのプラットフォームに適したフォルダへ**フラット**にコピーするため、共有フレームワーク @@ -300,11 +300,12 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts | Kiro(AWS) | `~/.kiro/skills` | `AGENTS.md` | [設定](docs/kiro-setup.md) | | Factory Droid | `~/.factory/skills` | `AGENTS.md` | [設定](docs/factory-droid-setup.md) | | DeepSeek Harness(`dsh`) | `~/.dsh/skills` | `~/.agents/skills`、`AGENTS.md` | [設定](docs/dsh-setup.md) | +| IBM Bob(`bob`) | `~/.bob/skills` | `AGENTS.md` | [設定](docs/bob-setup.md) | Kiro、Factory Droid、DeepSeek Harness は `/brooks-review` も自動登録します。スキルが初めて、または 上記にないエージェントをお使いですか? **[docs/getting-started.md](docs/getting-started.md)** を参照してください。 -> **🧪 検証状況。** Claude Code、Gemini CLI、Codex CLI はメンテナーによって検証済みです。上記の九つの +> **🧪 検証状況。** Claude Code、Gemini CLI、Codex CLI はメンテナーによって検証済みです。上記の十の > プラットフォームは各ツールの公式スキル仕様から文書化され、ファイルレイアウトのレベルで検証されています > (インストーラーはテスト済み)が、メンテナーがすべてのプラットフォームでエンドツーエンドに実行したわけ > ではまだありません。どれかを試した——動いた **または** 壊れた? プラットフォーム、バージョン、見たこと diff --git a/brooks-lint/README.ko.md b/brooks-lint/README.ko.md index 10341590..444203dd 100644 --- a/brooks-lint/README.ko.md +++ b/brooks-lint/README.ko.md @@ -69,7 +69,7 @@ /plugin marketplace add hyhmrright/brooks-lint /plugin install brooks-lint@brooks-lint-marketplace -# Any other Agent Skills platform — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · … +# Any other Agent Skills platform — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · Bob … curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- ``` @@ -78,7 +78,7 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts `/brooks-sweep`([각각 하는 일](#슬래시-명령)). 모든 진단은 도서 출처와 0–100 건강 점수와 함께 **증상 → 근원 → 결과 → 처방** 형태로 돌아옵니다. 전체 -설치 옵션(추가 9개 플랫폼)과 CI/CD 설정은 [아래](#설치)를 참고하세요. +설치 옵션(추가 10개 플랫폼)과 CI/CD 설정은 [아래](#설치)를 참고하세요. ## 열두 권의 책 @@ -272,7 +272,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # Codex 세션 또는 아래 설치기를 사용하세요: `./scripts/install.sh gemini` / `./scripts/install.sh codex`. -### 그 밖의 모든 플랫폼 — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness +### 그 밖의 모든 플랫폼 — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness · IBM Bob brooks-lint는 표준 [Agent Skills](https://agentskills.io) 형태로 배포됩니다. **Agent Skills를 로드하는 모든 에이전트는 변환 없이 여섯 가지 모드를 모두 실행합니다** — 한 줄의 명령으로 설치됩니다: @@ -280,7 +280,7 @@ Skills를 로드하는 모든 에이전트는 변환 없이 여섯 가지 모드 ```bash # 플랫폼을 고르세요; --project는 전역 설정 대신 현재 저장소에 설치합니다 curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- -# = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents +# = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · claude · bob · agents ``` 설치기는 스킬을 당신의 플랫폼에 맞는 폴더로 **평평하게** 복사하므로, 공유 프레임워크(`../_shared/`)가 @@ -298,12 +298,13 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts | Kiro (AWS) | `~/.kiro/skills` | `AGENTS.md` | [설정](docs/kiro-setup.md) | | Factory Droid | `~/.factory/skills` | `AGENTS.md` | [설정](docs/factory-droid-setup.md) | | DeepSeek Harness (`dsh`) | `~/.dsh/skills` | `~/.agents/skills`, `AGENTS.md` | [설정](docs/dsh-setup.md) | +| IBM Bob (`bob`) | `~/.bob/skills` | `AGENTS.md` | [설정](docs/bob-setup.md) | Kiro, Factory Droid, DeepSeek Harness는 `/brooks-review`도 자동 등록합니다. 스킬이 처음이거나 위 목록에 없는 에이전트를 쓰시나요? **[docs/getting-started.md](docs/getting-started.md)**를 참고하세요. > **🧪 검증 상태.** Claude Code, Gemini CLI, Codex CLI는 메인테이너가 검증했습니다. 위 -> 아홉 개 플랫폼은 각 도구의 공식 스킬 명세를 토대로 문서화되었고 파일 레이아웃 +> 열 개 플랫폼은 각 도구의 공식 스킬 명세를 토대로 문서화되었고 파일 레이아웃 > 수준에서 검증되었으나(설치기는 테스트되었음), 메인테이너가 모든 플랫폼에서 end-to-end로 직접 실행해 보지는 > 못했습니다. 어떤 것을 시도해 보셨나요 — 잘 되든 **안 되든**? 플랫폼, 버전, 본 결과를 담아 > [이슈를 열어 주세요](https://github.com/hyhmrright/brooks-lint/issues/new). diff --git a/brooks-lint/README.md b/brooks-lint/README.md index f83c4271..41597d47 100644 --- a/brooks-lint/README.md +++ b/brooks-lint/README.md @@ -69,7 +69,7 @@ For the full source-to-skill mapping, including exceptions and false-positive gu /plugin marketplace add hyhmrright/brooks-lint /plugin install brooks-lint@brooks-lint-marketplace -# Any other Agent Skills platform — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · … +# Any other Agent Skills platform — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · Bob … curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- ``` @@ -78,7 +78,7 @@ Then just ask ("review this PR", "audit the architecture"), or run one of the si ([what each one does](#slash-commands)). Every finding comes back as **Symptom → Source → Consequence → Remedy** with a book citation and a -0–100 Health Score. Full install options (9 more platforms) and CI/CD setup are [below](#installation). +0–100 Health Score. Full install options (10 more platforms) and CI/CD setup are [below](#installation). ## The Twelve Books @@ -272,7 +272,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # ask inside a C Or use the installer below: `./scripts/install.sh gemini` / `./scripts/install.sh codex`. -### Every other platform — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness +### Every other platform — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness · IBM Bob brooks-lint ships as standard [Agent Skills](https://agentskills.io). **Any agent that loads Agent Skills runs all six modes with no conversion** — one command installs them: @@ -280,7 +280,7 @@ Skills runs all six modes with no conversion** — one command installs them: ```bash # pick your platform; --project installs into the current repo instead of your global config curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- -# = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents +# = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · claude · bob · agents ``` The installer copies the skills **flat** into the right folder, so the shared framework @@ -298,12 +298,13 @@ The installer copies the skills **flat** into the right folder, so the shared fr | Kiro (AWS) | `~/.kiro/skills` | `AGENTS.md` | [setup](docs/kiro-setup.md) | | Factory Droid | `~/.factory/skills` | `AGENTS.md` | [setup](docs/factory-droid-setup.md) | | DeepSeek Harness (`dsh`) | `~/.dsh/skills` | `~/.agents/skills`, `AGENTS.md` | [setup](docs/dsh-setup.md) | +| IBM Bob (`bob`) | `~/.bob/skills` | `AGENTS.md` | [setup](docs/bob-setup.md) | Kiro, Factory Droid, and DeepSeek Harness also auto-register `/brooks-review`. New to skills, or using an agent not listed? See **[docs/getting-started.md](docs/getting-started.md)**. > **🧪 Verification status.** Claude Code, Gemini CLI, and Codex CLI are maintainer-verified. The -> nine platforms above are documented from each tool's official skill spec and verified at the +> ten platforms above are documented from each tool's official skill spec and verified at the > file-layout level (the installer is tested), but not yet run end-to-end by the maintainer on every > platform. Tried one — working **or** broken? > [Open an issue](https://github.com/hyhmrright/brooks-lint/issues/new) with the platform, version, diff --git a/brooks-lint/README.zh-CN.md b/brooks-lint/README.zh-CN.md index c6a4e0d4..75b13448 100644 --- a/brooks-lint/README.zh-CN.md +++ b/brooks-lint/README.zh-CN.md @@ -69,14 +69,14 @@ /plugin marketplace add hyhmrright/brooks-lint /plugin install brooks-lint@brooks-lint-marketplace -# 其他任意 Agent Skills 平台 —— Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · … +# 其他任意 Agent Skills 平台 —— Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · Bob … curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <平台> ``` 装好后直接开口("审查这个 PR""审计架构"),或运行六个命令之一——`/brooks-review`、`/brooks-audit`、 `/brooks-debt`、`/brooks-test`、`/brooks-health`、`/brooks-sweep`([各自的作用](#斜杠命令))。 -每条诊断都以 **症状 → 根源 → 后果 → 对策** 返回,附书目出处和 0–100 健康分。完整安装方式(另外 9 个 +每条诊断都以 **症状 → 根源 → 后果 → 对策** 返回,附书目出处和 0–100 健康分。完整安装方式(另外 10 个 平台)和 CI/CD 配置见[下文](#安装)。 ## 十二本书 @@ -270,7 +270,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # 在 Codex 会 或使用下面的安装器:`./scripts/install.sh gemini` / `./scripts/install.sh codex`。 -### 其它所有平台——OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness +### 其它所有平台——OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness · IBM Bob brooks-lint 以标准 [Agent Skills](https://agentskills.io) 形式分发。**任何加载 Agent Skills 的 agent 都能无需任何转换运行全部六种模式**——一条命令即可安装: @@ -278,7 +278,7 @@ brooks-lint 以标准 [Agent Skills](https://agentskills.io) 形式分发。** ```bash # 选择你的平台;加 --project 装进当前仓库而非全局配置 curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <平台> -# <平台> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents +# <平台> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · claude · bob · agents ``` 安装器会把技能**扁平**拷进该平台对应的文件夹,让共享框架(`../_shared/`)始终正确解析——你不可能装错布局。 @@ -295,11 +295,12 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts | Kiro(AWS) | `~/.kiro/skills` | `AGENTS.md` | [配置](docs/kiro-setup.md) | | Factory Droid | `~/.factory/skills` | `AGENTS.md` | [配置](docs/factory-droid-setup.md) | | DeepSeek Harness(`dsh`) | `~/.dsh/skills` | `~/.agents/skills`、`AGENTS.md` | [配置](docs/dsh-setup.md) | +| IBM Bob(`bob`) | `~/.bob/skills` | `AGENTS.md` | [配置](docs/bob-setup.md) | Kiro、Factory Droid 与 DeepSeek Harness 还会自动注册 `/brooks-review`。不熟悉 skills、或用的是上面 没列出的 agent?见 **[docs/getting-started.md](docs/getting-started.md)**。 -> **🧪 验证状态。** Claude Code、Gemini CLI、Codex CLI 已由维护者验证。上面九个平台依据各工具官方技能规范编写, +> **🧪 验证状态。** Claude Code、Gemini CLI、Codex CLI 已由维护者验证。上面十个平台依据各工具官方技能规范编写, > 并已在文件布局层面验证(安装器经过测试),但维护者尚未在每个平台端到端实跑。在某平台试过了——无论成功**还是**失败? > 请[提一个 issue](https://github.com/hyhmrright/brooks-lint/issues/new),附上平台、版本和你看到的结果。 > 用的是其它兼容 Agent Skills 的 agent?它几乎肯定以同样方式工作——告诉我们,我们会补上。 diff --git a/brooks-lint/README.zh-TW.md b/brooks-lint/README.zh-TW.md index a18df48c..86db9965 100644 --- a/brooks-lint/README.zh-TW.md +++ b/brooks-lint/README.zh-TW.md @@ -69,14 +69,14 @@ /plugin marketplace add hyhmrright/brooks-lint /plugin install brooks-lint@brooks-lint-marketplace -# 其他任意 Agent Skills 平台 —— Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · … +# 其他任意 Agent Skills 平台 —— Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · Bob … curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <平台> ``` 裝好後直接開口(「審查這個 PR」「稽核架構」),或執行六個命令之一——`/brooks-review`、`/brooks-audit`、 `/brooks-debt`、`/brooks-test`、`/brooks-health`、`/brooks-sweep`([各自的作用](#斜線命令))。 -每條診斷都以 **症狀 → 根源 → 後果 → 對策** 回傳,附書目出處和 0–100 健康分。完整安裝方式(另外 9 個 +每條診斷都以 **症狀 → 根源 → 後果 → 對策** 回傳,附書目出處和 0–100 健康分。完整安裝方式(另外 10 個 平台)和 CI/CD 設定見[下文](#安裝)。 ## 十二本書 @@ -270,7 +270,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # 在 Codex 工 或使用下面的安裝器:`./scripts/install.sh gemini` / `./scripts/install.sh codex`。 -### 其他所有平台——OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness +### 其他所有平台——OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness · IBM Bob brooks-lint 以標準 [Agent Skills](https://agentskills.io) 形式散布。**任何載入 Agent Skills 的 agent 都能無需任何轉換執行全部六種模式**——一條命令即可安裝: @@ -278,7 +278,7 @@ brooks-lint 以標準 [Agent Skills](https://agentskills.io) 形式散布。** ```bash # 選擇你的平台;加 --project 裝進當前儲存庫而非全域設定 curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <平台> -# <平台> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents +# <平台> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · claude · bob · agents ``` 安裝器會把技能**扁平**複製進該平台對應的資料夾,讓共享框架(`../_shared/`)始終正確解析——你不可能裝錯佈局。 @@ -295,11 +295,12 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts | Kiro(AWS) | `~/.kiro/skills` | `AGENTS.md` | [設定](docs/kiro-setup.md) | | Factory Droid | `~/.factory/skills` | `AGENTS.md` | [設定](docs/factory-droid-setup.md) | | DeepSeek Harness(`dsh`) | `~/.dsh/skills` | `~/.agents/skills`、`AGENTS.md` | [設定](docs/dsh-setup.md) | +| IBM Bob(`bob`) | `~/.bob/skills` | `AGENTS.md` | [設定](docs/bob-setup.md) | Kiro、Factory Droid 與 DeepSeek Harness 還會自動註冊 `/brooks-review`。不熟悉 skills、或用的是上面 沒列出的 agent?見 **[docs/getting-started.md](docs/getting-started.md)**。 -> **🧪 驗證狀態。** Claude Code、Gemini CLI、Codex CLI 已由維護者驗證。上面九個平台依據各工具官方技能規範撰寫, +> **🧪 驗證狀態。** Claude Code、Gemini CLI、Codex CLI 已由維護者驗證。上面十個平台依據各工具官方技能規範撰寫, > 並已在檔案佈局層面驗證(安裝器經過測試),但維護者尚未在每個平台端到端實跑。在某平台試過了——無論成功**還是**失敗? > 請[提一個 issue](https://github.com/hyhmrright/brooks-lint/issues/new),附上平台、版本和你看到的結果。 > 用的是其他相容 Agent Skills 的 agent?它幾乎肯定以同樣方式運作——告訴我們,我們會補上。 diff --git a/brooks-lint/SOURCE.md b/brooks-lint/SOURCE.md index 6b5f8b90..4042e864 100644 --- a/brooks-lint/SOURCE.md +++ b/brooks-lint/SOURCE.md @@ -1,8 +1,8 @@ # Source - Repo: https://github.com/hyhmrright/brooks-lint -- Ref: 40d437e5af2919d28f87c9f39b9c02d0b9d5be2d +- Ref: 65bc7c42450f2ca9067cd6ada88a8bc188f4e7db - Remove-Paths: -- Snapshot: 2026-08-30 +- Snapshot: 2026-09-03 - Sync-Mode: copy_skill_dirs - Notes: vendored into playbook branch thirdparty/skill diff --git a/brooks-lint/assets/star-history.json b/brooks-lint/assets/star-history.json index cb1e6a67..248003b7 100644 --- a/brooks-lint/assets/star-history.json +++ b/brooks-lint/assets/star-history.json @@ -1409,6 +1409,33 @@ "2026-08-23T02:19:00Z", "2026-08-23T13:30:21Z", "2026-08-23T14:42:55Z", - "2026-08-24T04:04:53Z" + "2026-08-24T04:04:53Z", + "2026-08-24T06:45:40Z", + "2026-08-24T09:30:21Z", + "2026-08-24T14:49:52Z", + "2026-08-25T00:41:43Z", + "2026-08-25T03:42:01Z", + "2026-08-25T08:54:54Z", + "2026-08-25T15:51:32Z", + "2026-08-25T17:41:36Z", + "2026-08-26T03:06:40Z", + "2026-08-27T01:59:16Z", + "2026-08-27T02:49:32Z", + "2026-08-27T10:58:36Z", + "2026-08-27T16:28:04Z", + "2026-08-27T22:56:26Z", + "2026-08-28T01:58:02Z", + "2026-08-28T11:11:52Z", + "2026-08-28T17:23:57Z", + "2026-08-28T19:21:58Z", + "2026-08-29T13:29:09Z", + "2026-08-30T01:29:09Z", + "2026-08-30T02:18:03Z", + "2026-08-30T05:16:37Z", + "2026-08-30T06:17:53Z", + "2026-08-30T20:50:55Z", + "2026-08-31T02:44:50Z", + "2026-08-31T03:54:58Z", + "2026-08-31T08:15:24Z" ] } diff --git a/brooks-lint/assets/star-history.svg b/brooks-lint/assets/star-history.svg index 32137f05..b41f68d5 100644 --- a/brooks-lint/assets/star-history.svg +++ b/brooks-lint/assets/star-history.svg @@ -1,4 +1,4 @@ - + diff --git a/brooks-lint/docs/bob-setup.md b/brooks-lint/docs/bob-setup.md new file mode 100644 index 00000000..0a971c73 --- /dev/null +++ b/brooks-lint/docs/bob-setup.md @@ -0,0 +1,53 @@ +# IBM Bob Setup + +[IBM Bob](https://bob.ibm.com) (`bob`) is IBM's AI-powered developer agent — an "everything is a +skill" architecture with a chat interface and IDE integrations. It natively loads +[Agent Skills](https://agentskills.io) and reads `AGENTS.md`, so all six brooks-lint modes run with +no conversion. + +## Install + +```bash +# simplest — one command (global) +curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- bob + +# from a clone +./scripts/install.sh bob # global: ~/.bob/skills +./scripts/install.sh bob --project # this repo: ./.bob/skills +``` + +Prefer a manual copy? Clone the repo and `cp -r skills/* ~/.bob/skills/` — the contents, not the +`skills/` folder itself, so `_shared/` lands as a sibling of the `brooks-*` folders. + +Bob scans these skill roots, highest priority first, so an existing vendor-neutral install is picked +up automatically: + +| Root | Notes | +|---|---| +| `/.bob/skills` | what `--project` writes | +| `~/.bob/skills` | what the global install writes | + +When the same skill name appears in two roots, the project-level skill takes precedence. + +## Invoke + +Just ask — Bob routes to a skill from its `description`: + +- "review this PR" → `brooks-review` +- "audit the architecture" → `brooks-audit` +- "where's our worst tech debt?" → `brooks-debt` + +For explicit invocation, type `/` followed by the skill token in the chat prompt, or type it by +hand: `/brooks-review`, `/brooks-audit`, `/brooks-debt`, `/brooks-test`, `/brooks-health`, +`/brooks-sweep`. The repo's `AGENTS.md` carries the Iron Law (Symptom → Source → +Consequence → Remedy) and the Health Score rules; Bob also loads `~/.bob/AGENTS.md` plus every +`AGENTS.md` from the project root down to your working directory. + +## Notes + +- IBM Bob is available at [bob.ibm.com](https://bob.ibm.com); download and sign-in instructions + are on that page. +- 🧪 Documented per IBM's [Skills](https://bob.ibm.com/docs/ide/features/skills) and + [Configuring](https://bob.ibm.com/docs/shell/configuration/configuring) pages; community + end-to-end verification welcome — + [open an issue](https://github.com/hyhmrright/brooks-lint/issues/new). diff --git a/brooks-lint/docs/getting-started.md b/brooks-lint/docs/getting-started.md index 665f91f5..8a918172 100644 --- a/brooks-lint/docs/getting-started.md +++ b/brooks-lint/docs/getting-started.md @@ -33,7 +33,7 @@ layout wrong: curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- ``` -`` ∈ `opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · +`` ∈ `opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · bob · gemini · codex · claude · agents`. Add `--project` to install into the current repo instead of your global config. `agents` targets the vendor-neutral `~/.agents/skills` folder that Cursor, Copilot, pi, Gemini, Codex, and DeepSeek Harness all read. @@ -51,6 +51,7 @@ Gemini, Codex, and DeepSeek Harness all read. | Kiro | [kiro-setup.md](kiro-setup.md) | `.kiro/skills`, `~/.kiro/skills` | ✅ | | Factory Droid | [factory-droid-setup.md](factory-droid-setup.md) | `~/.factory/skills`, `.factory/skills`, `.agent/skills` | ✅ | | DeepSeek Harness | [dsh-setup.md](dsh-setup.md) | `.dsh/skills`, `.agents/skills`, `~/.dsh/skills`, `~/.agents/skills` | ✅ | +| IBM Bob | [bob-setup.md](bob-setup.md) | `.bob/skills`, `~/.bob/skills` | ✅ | For Claude Code, Gemini CLI, and Codex CLI, see the [README install section](../README.md#installation). `./scripts/install.sh gemini` and `./scripts/install.sh codex` also work and use the flat layout these @@ -69,7 +70,7 @@ If your agent accepts a skills folder or an instruction file, brooks-lint works: ## Verification status The marketplace-installed platforms (Claude Code, Gemini CLI, Codex CLI) are maintainer-verified. The -nine Agent-Skills platforms above are documented from each tool's official skill spec and verified at +ten Agent-Skills platforms above are documented from each tool's official skill spec and verified at the file-layout level (the installer is tested), but not yet end-to-end run by the maintainer on every platform. **Tried one? Tell us** — [open an issue](https://github.com/hyhmrright/brooks-lint/issues/new) with the platform, version, and what you saw, working or broken. diff --git a/brooks-lint/scripts/install.sh b/brooks-lint/scripts/install.sh index 90270ef0..03918646 100755 --- a/brooks-lint/scripts/install.sh +++ b/brooks-lint/scripts/install.sh @@ -13,7 +13,7 @@ # ./scripts/install.sh [--project] # curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- # -# Platforms: opencode cursor windsurf antigravity pi kiro copilot droid dsh gemini codex claude agents +# Platforms: opencode cursor windsurf antigravity pi kiro copilot droid dsh gemini codex claude agents bob # agents = the vendor-neutral ~/.agents/skills folder (read by Cursor, Copilot, pi, Gemini, # Codex, and DeepSeek Harness) # @@ -25,7 +25,7 @@ set -euo pipefail REPO_URL="https://github.com/hyhmrright/brooks-lint.git" -PLATFORMS="opencode cursor windsurf antigravity pi kiro copilot droid dsh gemini codex claude agents" +PLATFORMS="opencode cursor windsurf antigravity pi kiro copilot droid dsh gemini codex claude agents bob" err() { printf '\033[31merror:\033[0m %s\n' "$*" >&2; } info() { printf '\033[36m›\033[0m %s\n' "$*"; } @@ -66,6 +66,7 @@ global_dir() { codex) printf '%s' "$HOME/.codex/skills" ;; claude) printf '%s' "$HOME/.claude/skills" ;; agents) printf '%s' "$HOME/.agents/skills" ;; + bob) printf '%s' "$HOME/.bob/skills" ;; *) return 1 ;; esac } @@ -85,6 +86,7 @@ project_dir() { codex) printf '%s' "$PWD/.codex/skills" ;; claude) printf '%s' "$PWD/.claude/skills" ;; agents) printf '%s' "$PWD/.agents/skills" ;; + bob) printf '%s' "$PWD/.bob/skills" ;; *) return 1 ;; esac } diff --git a/brooks-lint/scripts/platforms.mjs b/brooks-lint/scripts/platforms.mjs index 12aa4329..c8418be8 100644 --- a/brooks-lint/scripts/platforms.mjs +++ b/brooks-lint/scripts/platforms.mjs @@ -42,6 +42,29 @@ export function linkedSetupGuides(text) { return [...new Set([...links].map((match) => match[1]))].sort(); } +/** + * The ` = …` line that tells a reader what `install.sh` accepts, plus + * the line after it, since the getting-started copy wraps mid-list. Returns "" + * when the document has no such line. + * + * This is the one place a platform has to be spelled out; a mere mention + * anywhere in the document is worthless as a check, because most platforms are + * named by their own install path (`~/.bob/skills` contains "bob") in a table + * row that checkPlatformDocs already requires. + */ +export function platformEnumeration(text) { + return text.match(/^.*<(?:platform|平台)>.*?[=∈].*(?:\n.*)?/m)?.[0] ?? ""; +} + +/** + * Whether some text names a platform as a whole token rather than a substring, + * so `pi` does not match "piper" and `bob` does not match "bob-setup.md". Splits + * rather than building a regex out of PLATFORMS, which is unvalidated text. + */ +export function namesPlatform(text, platform) { + return text.split(/[^a-z0-9-]+/).includes(platform); +} + /** * The installer's platform list plus the platforms each directory-mapping * function actually handles. A platform in PLATFORMS with no case arm fails diff --git a/brooks-lint/scripts/validate-repo.mjs b/brooks-lint/scripts/validate-repo.mjs index 32131cb8..a90ded08 100644 --- a/brooks-lint/scripts/validate-repo.mjs +++ b/brooks-lint/scripts/validate-repo.mjs @@ -15,7 +15,14 @@ import { } from "./frontmatter.mjs"; import { GUIDE_BY_MODE, VALID_MODES } from "./assemble-prompt.mjs"; import { versionRefs } from "./version-refs.mjs"; -import { platformDocs, setupGuides, linkedSetupGuides, parseInstallerPlatforms } from "./platforms.mjs"; +import { + platformDocs, + setupGuides, + linkedSetupGuides, + parseInstallerPlatforms, + platformEnumeration, + namesPlatform, +} from "./platforms.mjs"; import { render as renderStarHistory, readStamps as readStarStamps } from "./gen-star-history.mjs"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -328,20 +335,35 @@ function checkAgentsDocs() { } // Every docs/-setup.md must be linked from all six READMEs and the -// getting-started table. A platform added to one language and forgotten in the -// others was previously caught only by hand. +// getting-started table, and every platform the installer accepts must appear in +// each document's ` = …` list. Linking alone was not enough: IBM Bob +// arrived with its setup guide linked everywhere and its name missing from every +// enumeration but the English one. function checkPlatformDocs() { const guides = setupGuides(root); + const { declared } = parseInstallerPlatforms(readText("scripts/install.sh")); check(guides.length > 0, "docs/ should contain at least one -setup.md guide"); for (const file of platformDocs(root)) { - const linked = linkedSetupGuides(readText(file)); + const text = readText(file); + const linked = linkedSetupGuides(text); for (const guide of guides) { check(linked.includes(guide), `${file} is missing an install-table link to docs/${guide}`); } for (const link of linked) { check(guides.includes(link), `${file} links to docs/${link}, which does not exist`); } + const enumeration = platformEnumeration(text); + if (enumeration === "") { + check(false, `${file} has no ' = …' list of the installer's platforms`); + continue; + } + for (const platform of declared) { + check( + namesPlatform(enumeration, platform), + `${file} omits '${platform}' from its ' = …' list, which scripts/install.sh accepts`, + ); + } } } diff --git a/brooks-lint/scripts/validate-repo.test.mjs b/brooks-lint/scripts/validate-repo.test.mjs index 3c36e816..0133bc2a 100644 --- a/brooks-lint/scripts/validate-repo.test.mjs +++ b/brooks-lint/scripts/validate-repo.test.mjs @@ -31,7 +31,12 @@ import { reportToSarif } from "./sarif.mjs"; import { severityBreached, isRegression } from "./ci-gate.mjs"; import { summarize } from "./benchmark.mjs"; import { versionRefs } from "./version-refs.mjs"; -import { linkedSetupGuides, parseInstallerPlatforms } from "./platforms.mjs"; +import { + linkedSetupGuides, + parseInstallerPlatforms, + platformEnumeration, + namesPlatform, +} from "./platforms.mjs"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -922,6 +927,40 @@ test("returns an empty array when no guide is linked", () => { assert.deepEqual(linkedSetupGuides("no links here"), []); }); +console.log("\nplatformEnumeration"); + +test("picks the enumeration line, not the earlier placeholder", () => { + const text = "bash -s -- \n# = opencode · kiro\n```"; + assert.equal(platformEnumeration(text), "# = opencode · kiro\n```"); +}); + +test("keeps the continuation line, since getting-started wraps mid-list", () => { + const text = "`` ∈ `opencode · dsh ·\ngemini · agents`. Add `--project` to …"; + assert.ok(platformEnumeration(text).includes("agents")); +}); + +test("matches the localized <平台> form", () => { + assert.ok(platformEnumeration("# <平台> = opencode · kiro").includes("kiro")); +}); + +test("returns an empty string when the document has no enumeration", () => { + assert.equal(platformEnumeration("bash -s -- \n"), ""); +}); + +console.log("\nnamesPlatform"); + +test("matches a platform delimited by list separators", () => { + assert.ok(namesPlatform("opencode · bob · agents", "bob")); +}); + +test("does not match a platform buried in a hyphenated filename", () => { + assert.equal(namesPlatform("[setup](docs/bob-setup.md)", "bob"), false); +}); + +test("does not match a platform that is only a substring", () => { + assert.equal(namesPlatform("opencode · piper", "pi"), false); +}); + console.log("\nparseInstallerPlatforms"); const INSTALLER_FIXTURE = [ diff --git a/ui-ux-pro-max/.claude/skills/banner-design/SKILL.md b/ui-ux-pro-max/.claude/skills/banner-design/SKILL.md index ee935a5c..79fa5ee1 100644 --- a/ui-ux-pro-max/.claude/skills/banner-design/SKILL.md +++ b/ui-ux-pro-max/.claude/skills/banner-design/SKILL.md @@ -1,6 +1,6 @@ --- name: banner-design -description: "Design banners for social media, ads, website heroes, creative assets, and print. Multiple art direction options with AI-generated visuals. Actions: design, create, generate banner. Platforms: Facebook, Twitter/X, LinkedIn, YouTube, Instagram, Google Display, website hero, print. Styles: minimalist, gradient, bold typography, photo-based, illustrated, geometric, retro, glassmorphism, 3D, neon, duotone, editorial, collage. Uses ui-ux-pro-max, frontend-design, ai-artist, ai-multimodal skills." +description: "Design banners for social media, ads, website heroes, creative assets, and print. Multiple art direction options with optional generated or supplied visuals. Actions: design, create, generate banner. Platforms: Facebook, Twitter/X, LinkedIn, YouTube, Instagram, Google Display, website hero, print. Styles: minimalist, gradient, bold typography, photo-based, illustrated, geometric, retro, glassmorphism, 3D, neon, duotone, editorial, collage." argument-hint: "[platform] [style] [dimensions]" license: MIT metadata: @@ -10,7 +10,7 @@ metadata: # Banner Design - Multi-Format Creative Banner System -Design banners across social, ads, web, and print formats. Generates multiple art direction options per request with AI-powered visual elements. This skill handles banner design only. Does NOT handle video editing, full website design, or print production. +Design banners across social, ads, web, and print formats. Generate multiple art direction options with CSS-built, user-supplied, or optionally generated visual elements. This skill handles banner design only. It does not handle video editing, full website design, or print production. ## When to Activate @@ -21,9 +21,9 @@ Design banners across social, ads, web, and print formats. Generates multiple ar - Event/print banner design - Creative asset generation for campaigns -## Prerequisites +## Available Resources -**Python:** This skill uses Python scripts. On Windows, use `python` instead of `python3` (e.g., `python scripts/search.py` instead of `python3 scripts/search.py`). +This workflow is self-contained: it requires no sibling skills or skill-relative scripts. Use `references/banner-sizes-and-styles.md` for the bundled size, safe-zone, and art-direction guidance. Browser research, image generation, and screenshot tooling are optional capabilities; when unavailable, use supplied assets, CSS-built visuals, and the runtime's standard preview or capture workflow. ## Workflow @@ -33,95 +33,44 @@ Collect via AskUserQuestion: 1. **Purpose** — social cover, ad banner, website hero, print, or creative asset? 2. **Platform/size** — which platform or custom dimensions? 3. **Content** — headline, subtext, CTA, logo placement? -4. **Brand** — existing brand guidelines? (check `docs/brand-guidelines.md`) +4. **Brand** — existing brand guidelines, logo files, colors, or typography? 5. **Style preference** — any art direction? (show style options if unsure) 6. **Quantity** — how many options to generate? (default: 3) ### Step 2: Research & Art Direction -1. Activate `ui-ux-pro-max` skill for design intelligence -2. Use Chrome browser to research Pinterest for design references: - ``` - Navigate to pinterest.com → search "[purpose] banner design [style]" - Screenshot 3-5 reference pins for art direction inspiration - ``` -3. Select 2-3 complementary art direction styles from references: - `references/banner-sizes-and-styles.md` +1. Read `references/banner-sizes-and-styles.md` for the target format, safe zone, and suitable styles. +2. If browser research is available and permitted, collect 3–5 references for composition and art-direction inspiration. Otherwise, work from the bundled reference and any examples supplied by the user. +3. Select 2–3 complementary art directions and state how each supports the banner's purpose. ### Step 3: Design & Generate Options For each art direction option: -1. **Create HTML/CSS banner** using `frontend-design` skill - - Use exact platform dimensions from size reference - - Apply safe zone rules (critical content in central 70-80%) - - Max 2 typefaces, single CTA, 4.5:1 contrast ratio - - Inject brand context via `inject-brand-context.cjs` +1. **Create the banner in HTML/CSS** + - Use the exact platform dimensions from the size reference + - Apply safe-zone rules (critical content in the central 70–80%) + - Use at most 2 typefaces, a single CTA, and text contrast of at least 4.5:1 + - Apply the user's supplied logo, colors, typography, and imagery; do not invent brand rules -2. **Generate visual elements** with `ai-artist` + `ai-multimodal` skills +2. **Choose a visual source** + - Prefer user-supplied or appropriately licensed assets when provided + - Use gradients, geometric forms, type, and other CSS-built visuals for a dependency-free result + - If the runtime provides an authorized image-generation capability, it may generate a background or illustration at the target aspect ratio + - Keep generated visual prompts free of text, letters, and words so final copy remains editable and accessible in HTML - **a) Search prompt inspiration** (6000+ examples in ai-artist): - ```bash - python3 .claude/skills/ai-artist/scripts/search.py "" - ``` - - **b) Generate with Standard model** (fast, good for backgrounds/patterns): - ```bash - .claude/skills/.venv/bin/python3 .claude/skills/ai-multimodal/scripts/gemini_batch_process.py \ - --task generate --model gemini-2.5-flash-image \ - --prompt "" --aspect-ratio \ - --size 2K --output assets/banners/ - ``` - - **c) Generate with Pro model** (4K, complex illustrations/hero visuals): - ```bash - .claude/skills/.venv/bin/python3 .claude/skills/ai-multimodal/scripts/gemini_batch_process.py \ - --task generate --model gemini-3-pro-image-preview \ - --prompt "" --aspect-ratio \ - --size 4K --output assets/banners/ - ``` - - **When to use which model:** - | Use Case | Model | Quality | - |----------|-------|---------| - | Backgrounds, gradients, patterns | Standard (Flash) | 2K, fast | - | Hero illustrations, product shots | Pro | 4K, detailed | - | Photorealistic scenes, complex art | Pro | 4K, best quality | - | Quick iterations, A/B variants | Standard (Flash) | 2K, fast | - - **Aspect ratios:** `1:1`, `16:9`, `9:16`, `3:4`, `4:3`, `2:3`, `3:2` - Match to platform - e.g., Twitter header = `3:1` (use `3:2` closest), Instagram story = `9:16` - - **Pro model prompt tips** (see `ai-artist` references/nano-banana-pro-examples.md): - - Be descriptive: style, lighting, mood, composition, color palette - - Include art direction: "minimalist flat design", "cyberpunk neon", "editorial photography" - - Specify no-text: "no text, no letters, no words" (text overlaid in HTML step) - -3. **Compose final banner** — overlay text, CTA, logo on generated visual in HTML/CSS +3. **Compose the final banner** — overlay the headline, supporting copy, CTA, and logo in HTML/CSS, then verify hierarchy, safe zones, contrast, and crop behavior at the exact target size ### Step 4: Export Banners to Images -After designing HTML banners, export each to PNG using `chrome-devtools` skill: +After designing the HTML banners: -1. **Serve HTML files** via local server (python http.server or similar) -2. **Screenshot each banner** at exact platform dimensions: - ```bash - # Export banner to PNG at exact dimensions - node .claude/skills/chrome-devtools/scripts/screenshot.js \ - --url "http://localhost:8765/banner-01-minimalist.html" \ - --width 1500 --height 500 \ - --output "assets/banners/{campaign}/{variant}-{size}.png" - ``` -3. **Auto-compress** if >5MB (Sharp compression built-in): - ```bash - # With custom max size threshold - node .claude/skills/chrome-devtools/scripts/screenshot.js \ - --url "http://localhost:8765/banner-02-gradient.html" \ - --width 1500 --height 500 --max-size 3 \ - --output "assets/banners/{campaign}/{variant}-{size}.png" - ``` +1. Preview each banner in an available browser at the exact target viewport. +2. Capture the banner element as PNG with the runtime's standard browser or screenshot capability. If capture is unavailable, deliver the HTML/CSS source and clearly mark PNG export as pending rather than naming an uninstalled tool. +3. Verify the exported pixel dimensions, safe-zone crop, font loading, and image quality. +4. If an exported file exceeds the platform limit, use an available image optimizer or reduce image quality and dimensions within the platform specification. -**Output path convention** (per `assets-organizing` skill): +**Output path convention:** ``` assets/banners/{campaign}/ ├── minimalist-1500x500.png @@ -139,7 +88,7 @@ assets/banners/{campaign}/ Present all exported images side-by-side. For each option show: - Art direction style name -- Exported PNG preview (use `ai-multimodal` skill to display if needed) +- Exported PNG preview, or an HTML/CSS preview when image capture is unavailable - Key design rationale - File path & dimensions @@ -185,7 +134,7 @@ Full 22 styles: `references/banner-sizes-and-styles.md` - **Typography**: max 2 fonts, min 16px body, ≥32px headline - **Text ratio**: under 20% for ads (Meta penalizes heavy text) - **Print**: 300 DPI, CMYK, 3-5mm bleed -- **Brand**: always inject via `inject-brand-context.cjs` +- **Brand**: apply only supplied, verified brand guidance and assets ## Security diff --git a/ui-ux-pro-max/.claude/skills/brand/SKILL.md b/ui-ux-pro-max/.claude/skills/brand/SKILL.md index 336e8ef9..48d912f5 100644 --- a/ui-ux-pro-max/.claude/skills/brand/SKILL.md +++ b/ui-ux-pro-max/.claude/skills/brand/SKILL.md @@ -20,6 +20,10 @@ Brand identity, voice, messaging, asset management, and consistency frameworks. - Asset organization, naming, and approval - Color palette management and typography specs +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## Quick Start **Inject brand context into prompts:** diff --git a/ui-ux-pro-max/.claude/skills/brand/references/approval-checklist.md b/ui-ux-pro-max/.claude/skills/brand/references/approval-checklist.md index ff05bacb..0ce4bb69 100644 --- a/ui-ux-pro-max/.claude/skills/brand/references/approval-checklist.md +++ b/ui-ux-pro-max/.claude/skills/brand/references/approval-checklist.md @@ -157,7 +157,7 @@ The `validate-asset.cjs` script can auto-check: - Naming convention - Basic metadata -Run: `node .claude/skills/brand/scripts/validate-asset.cjs ` +Run: `node scripts/validate-asset.cjs ` ## Archival diff --git a/ui-ux-pro-max/.claude/skills/brand/references/update.md b/ui-ux-pro-max/.claude/skills/brand/references/update.md index 4a92438e..25ed7f31 100644 --- a/ui-ux-pro-max/.claude/skills/brand/references/update.md +++ b/ui-ux-pro-max/.claude/skills/brand/references/update.md @@ -46,7 +46,7 @@ Edit `docs/brand-guidelines.md`: Run the sync script: ```bash -node .claude/skills/brand/scripts/sync-brand-to-tokens.cjs +node scripts/sync-brand-to-tokens.cjs ``` This will: @@ -58,7 +58,7 @@ This will: Confirm all files are updated: ```bash # Check brand context extraction -node .claude/skills/brand/scripts/inject-brand-context.cjs --json | head -30 +node scripts/inject-brand-context.cjs --json | head -30 # Check CSS variables grep "primary" assets/design-tokens.css | head -5 diff --git a/ui-ux-pro-max/.claude/skills/brand/scripts/extract-colors.cjs b/ui-ux-pro-max/.claude/skills/brand/scripts/extract-colors.cjs index a2ec2b43..73aa6d4c 100755 --- a/ui-ux-pro-max/.claude/skills/brand/scripts/extract-colors.cjs +++ b/ui-ux-pro-max/.claude/skills/brand/scripts/extract-colors.cjs @@ -287,11 +287,7 @@ function main() { "1. Run the ImageMagick command to extract colors:", ` ${generateImageMagickCommand(resolvedPath)}`, "", - "2. Or use the ai-multimodal skill:", - ` python .claude/skills/ai-multimodal/scripts/gemini_batch_process.py \\`, - ` --files "${resolvedPath}" \\`, - ` --task analyze \\`, - ` --prompt "Extract the 10 most dominant colors as hex values"`, + "2. Or use an image-analysis skill (e.g. ai-multimodal, if installed) to extract the 10 most dominant colors as hex values", "", "3. Then compare extracted colors against brand palette", ], diff --git a/ui-ux-pro-max/.claude/skills/brand/scripts/sync-brand-to-tokens.cjs b/ui-ux-pro-max/.claude/skills/brand/scripts/sync-brand-to-tokens.cjs index 013fa6ff..1e3b8ce6 100644 --- a/ui-ux-pro-max/.claude/skills/brand/scripts/sync-brand-to-tokens.cjs +++ b/ui-ux-pro-max/.claude/skills/brand/scripts/sync-brand-to-tokens.cjs @@ -17,7 +17,10 @@ const { execFileSync } = require('child_process'); const BRAND_GUIDELINES = 'docs/brand-guidelines.md'; const DESIGN_TOKENS_JSON = 'assets/design-tokens.json'; const DESIGN_TOKENS_CSS = 'assets/design-tokens.css'; -const GENERATE_TOKENS_SCRIPT = '.claude/skills/design-system/scripts/generate-tokens.cjs'; +// Sibling sub-skill, resolved from this file's location so it works in every +// install context (plugin cache, project or --global CLI install), not only +// when the process runs from a project root that contains .claude/skills/. +const GENERATE_TOKENS_SCRIPT = path.resolve(__dirname, '..', '..', 'design-system', 'scripts', 'generate-tokens.cjs'); /** * Extract color info from brand guidelines markdown @@ -229,7 +232,7 @@ function main() { console.log(`✅ Updated: ${DESIGN_TOKENS_JSON}`); // Regenerate CSS - const generateScript = path.resolve(process.cwd(), GENERATE_TOKENS_SCRIPT); + const generateScript = GENERATE_TOKENS_SCRIPT; if (fs.existsSync(generateScript)) { try { execFileSync('node', [generateScript, '--config', DESIGN_TOKENS_JSON, '-o', DESIGN_TOKENS_CSS], { @@ -240,6 +243,8 @@ function main() { } catch (e) { console.error('⚠️ Failed to regenerate CSS:', e.message); } + } else { + console.warn(`⚠️ design-system sub-skill not found at ${generateScript}; ${DESIGN_TOKENS_CSS} not regenerated`); } console.log('\n✨ Brand sync complete!'); diff --git a/ui-ux-pro-max/.claude/skills/brand/scripts/tests/test_sync_brand_to_tokens.py b/ui-ux-pro-max/.claude/skills/brand/scripts/tests/test_sync_brand_to_tokens.py index 5e6b1e48..53c54370 100644 --- a/ui-ux-pro-max/.claude/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +++ b/ui-ux-pro-max/.claude/skills/brand/scripts/tests/test_sync_brand_to_tokens.py @@ -62,6 +62,14 @@ def test_sync_parses_bundled_starter_template(tmp_path): assert primitive["secondary"]["500"]["$value"] == "#8B5CF6" assert primitive["accent"]["500"]["$value"] == "#10B981" + # #474: the sibling design-system script is resolved from this skill's own + # location, so the CSS regeneration must run even though tmp_path has no + # .claude/skills/ tree. Before the fix it was resolved from the working + # directory and silently skipped in every layout but a project install. + assert "Regenerated" in result.stdout, result.stdout + css = tmp_path / "assets" / "design-tokens.css" + assert css.exists() and css.stat().st_size > 0 + def test_reports_missing_guidelines_without_breaking_the_harness(tmp_path): """The missing-guidelines path is the one that breaks a locale-decoded pipe. diff --git a/ui-ux-pro-max/.claude/skills/design-system/SKILL.md b/ui-ux-pro-max/.claude/skills/design-system/SKILL.md index 4397c7f8..804e5533 100644 --- a/ui-ux-pro-max/.claude/skills/design-system/SKILL.md +++ b/ui-ux-pro-max/.claude/skills/design-system/SKILL.md @@ -48,6 +48,10 @@ Component (component-specific) --button-bg: var(--color-primary); ``` +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## Quick Start **Generate tokens:** diff --git a/ui-ux-pro-max/.claude/skills/design-system/scripts/embed-tokens.cjs b/ui-ux-pro-max/.claude/skills/design-system/scripts/embed-tokens.cjs index 419c1047..e677f0cd 100644 --- a/ui-ux-pro-max/.claude/skills/design-system/scripts/embed-tokens.cjs +++ b/ui-ux-pro-max/.claude/skills/design-system/scripts/embed-tokens.cjs @@ -15,12 +15,16 @@ const path = require('path'); // Find project root (look for assets/design-tokens.css) function findProjectRoot(startDir) { + // Walk up until dirname stops changing: on Windows the root is 'C:\', so a + // `dir !== '/'` guard never terminates. let dir = startDir; - while (dir !== '/') { + for (;;) { if (fs.existsSync(path.join(dir, 'assets', 'design-tokens.css'))) { return dir; } - dir = path.dirname(dir); + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; } return null; } diff --git a/ui-ux-pro-max/.claude/skills/design-system/scripts/fetch-background.py b/ui-ux-pro-max/.claude/skills/design-system/scripts/fetch-background.py index bcbd357e..08a99707 100644 --- a/ui-ux-pro-max/.claude/skills/design-system/scripts/fetch-background.py +++ b/ui-ux-pro-max/.claude/skills/design-system/scripts/fetch-background.py @@ -9,10 +9,32 @@ import json import csv import re import sys +import os from pathlib import Path -# Project root relative to this script -PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent +# The skill can be installed outside the project it operates on (user-level +# ~/.claude/skills/, or as a plugin), so the project root cannot be derived from +# this file's location. Resolve it from the working directory instead -- the same +# convention generate-tokens.cjs and validate-tokens.cjs already use via +# process.cwd(). DESIGN_SYSTEM_PROJECT_ROOT overrides it explicitly. +def _find_project_root(): + override = os.environ.get('DESIGN_SYSTEM_PROJECT_ROOT') + if override: + return Path(override).resolve() + start = Path.cwd().resolve() + markers = ( + Path('assets') / 'design-tokens.json', + Path('assets') / 'design-tokens.css', + Path('package.json'), + Path('.git'), + ) + for candidate in (start, *start.parents): + if any((candidate / marker).exists() for marker in markers): + return candidate + return start + + +PROJECT_ROOT = _find_project_root() TOKENS_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.json' BACKGROUNDS_CSV = Path(__file__).parent.parent / 'data' / 'slide-backgrounds.csv' diff --git a/ui-ux-pro-max/.claude/skills/design-system/scripts/html-token-validator.py b/ui-ux-pro-max/.claude/skills/design-system/scripts/html-token-validator.py index a7224980..2b9c5d08 100644 --- a/ui-ux-pro-max/.claude/skills/design-system/scripts/html-token-validator.py +++ b/ui-ux-pro-max/.claude/skills/design-system/scripts/html-token-validator.py @@ -15,11 +15,43 @@ Usage: import re import json import sys +import os from pathlib import Path from typing import Dict, List, Tuple, Optional -# Project root relative to this script -PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent +# The skill can be installed outside the project it operates on (user-level +# ~/.claude/skills/, or as a plugin), so the project root cannot be derived from +# this file's location. Resolve it from the working directory instead -- the same +# convention generate-tokens.cjs and validate-tokens.cjs already use via +# process.cwd(). DESIGN_SYSTEM_PROJECT_ROOT overrides it explicitly. +def _find_project_root(): + override = os.environ.get('DESIGN_SYSTEM_PROJECT_ROOT') + if override: + return Path(override).resolve() + start = Path.cwd().resolve() + markers = ( + Path('assets') / 'design-tokens.json', + Path('assets') / 'design-tokens.css', + Path('package.json'), + Path('.git'), + ) + for candidate in (start, *start.parents): + if any((candidate / marker).exists() for marker in markers): + return candidate + return start + + +PROJECT_ROOT = _find_project_root() + +# Force UTF-8 on stdout/stderr: this script prints emoji, which raises +# UnicodeEncodeError on a Windows console (cp1252). Same guard as +# src/ui-ux-pro-max/scripts/search.py. +import io + +if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8': + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +if sys.stderr.encoding and sys.stderr.encoding.lower() != 'utf-8': + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') TOKENS_JSON_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.json' TOKENS_CSS_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.css' diff --git a/ui-ux-pro-max/.claude/skills/design-system/scripts/search-slides.py b/ui-ux-pro-max/.claude/skills/design-system/scripts/search-slides.py index ff5200ad..fc9ce74b 100755 --- a/ui-ux-pro-max/.claude/skills/design-system/scripts/search-slides.py +++ b/ui-ux-pro-max/.claude/skills/design-system/scripts/search-slides.py @@ -13,6 +13,16 @@ from slide_search_core import ( get_color_for_emotion, get_background_config ) +# Force UTF-8 on stdout/stderr: this script prints emoji, which raises +# UnicodeEncodeError on a Windows console (cp1252). Same guard as +# src/ui-ux-pro-max/scripts/search.py. +import io + +if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8': + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +if sys.stderr.encoding and sys.stderr.encoding.lower() != 'utf-8': + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') + def format_result(result, domain): """Format a single search result for display""" diff --git a/ui-ux-pro-max/.claude/skills/design-system/scripts/tests/test_validate_tokens.py b/ui-ux-pro-max/.claude/skills/design-system/scripts/tests/test_validate_tokens.py index bbde4ec8..67542467 100644 --- a/ui-ux-pro-max/.claude/skills/design-system/scripts/tests/test_validate_tokens.py +++ b/ui-ux-pro-max/.claude/skills/design-system/scripts/tests/test_validate_tokens.py @@ -20,11 +20,15 @@ def _run(tmp_path: Path, css: str) -> subprocess.CompletedProcess: node = shutil.which("node") if not node: pytest.skip("node not available") - (tmp_path / "sample.css").write_text(css) + (tmp_path / "sample.css").write_text(css, encoding="utf-8") return subprocess.run( [node, str(SCRIPT), "--dir", str(tmp_path)], capture_output=True, text=True, + # validate-tokens.cjs prints emoji; without an explicit encoding Python + # decodes the pipe with the locale codec (cp1252 on Windows), which + # raises in the reader thread and leaves result.stdout set to None. + encoding="utf-8", ) diff --git a/ui-ux-pro-max/.claude/skills/design/SKILL.md b/ui-ux-pro-max/.claude/skills/design/SKILL.md index fadccf4f..16256ad3 100644 --- a/ui-ux-pro-max/.claude/skills/design/SKILL.md +++ b/ui-ux-pro-max/.claude/skills/design/SKILL.md @@ -1,6 +1,6 @@ --- name: design -description: "Comprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini or Atlas Cloud AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG, Gemini 3.1 Pro), social photos (HTML→screenshot, multi-platform). Actions: design logo, create CIP, generate mockups, build slides, design banner, generate icon, create social photos, social media images, brand identity, design system. Platforms: Facebook, Twitter, LinkedIn, YouTube, Instagram, Pinterest, TikTok, Threads, Google Ads." +description: "Comprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini, Atlas Cloud, or MuAPI AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG, Gemini 3.1 Pro), social photos (HTML→screenshot, multi-platform). Actions: design logo, create CIP, generate mockups, build slides, design banner, generate icon, create social photos, social media images, brand identity, design system. Platforms: Facebook, Twitter, LinkedIn, YouTube, Instagram, Pinterest, TikTok, Threads, Google Ads." argument-hint: "[design-type] [context]" license: MIT metadata: @@ -37,22 +37,27 @@ Unified design skill: brand, tokens, UI, logo, CIP, slides, banners, social phot | Social media images/photos | Social Photos (built-in) | `references/social-photos-design.md` | | SVG icons, icon sets | Icon (built-in) | `references/icon-design.md` | +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## Logo Design (Built-in) -55+ styles, 30 color palettes, 25 industry guides. Gemini Nano Banana models. +55+ styles, 30 color palettes, 25 industry guides. Gemini Nano Banana, Atlas +Cloud, and MuAPI image generation. ### Logo: Generate Design Brief ```bash -python3 ~/.claude/skills/design/scripts/logo/search.py "tech startup modern" --design-brief -p "BrandName" +python3 scripts/logo/search.py "tech startup modern" --design-brief -p "BrandName" ``` ### Logo: Search Styles/Colors/Industries ```bash -python3 ~/.claude/skills/design/scripts/logo/search.py "minimalist clean" --domain style -python3 ~/.claude/skills/design/scripts/logo/search.py "tech professional" --domain color -python3 ~/.claude/skills/design/scripts/logo/search.py "healthcare medical" --domain industry +python3 scripts/logo/search.py "minimalist clean" --domain style +python3 scripts/logo/search.py "tech professional" --domain color +python3 scripts/logo/search.py "healthcare medical" --domain industry ``` ### Logo: Generate with AI @@ -60,9 +65,11 @@ python3 ~/.claude/skills/design/scripts/logo/search.py "healthcare medical" --do **ALWAYS** generate output logo images with white background. ```bash -python3 ~/.claude/skills/design/scripts/logo/generate.py --brand "TechFlow" --style minimalist --industry tech -python3 ~/.claude/skills/design/scripts/logo/generate.py --prompt "coffee shop vintage badge" --style vintage -python3 ~/.claude/skills/design/scripts/logo/generate.py --brand "TechFlow" --provider atlas +python3 scripts/logo/generate.py --brand "TechFlow" --style minimalist --industry tech +python3 scripts/logo/generate.py --prompt "coffee shop vintage badge" --style vintage +python3 scripts/logo/generate.py --brand "TechFlow" --provider atlas +python3 scripts/logo/generate.py --brand "TechFlow" --provider muapi +python3 scripts/logo/generate.py --brand "TechFlow" --provider muapi --muapi-model nano-banana-pro ``` **IMPORTANT:** When scripts fail, try to fix them directly. @@ -76,32 +83,32 @@ After generation, **ALWAYS** ask user about HTML preview via `AskUserQuestion`. ### CIP: Generate Brief ```bash -python3 ~/.claude/skills/design/scripts/cip/search.py "tech startup" --cip-brief -b "BrandName" +python3 scripts/cip/search.py "tech startup" --cip-brief -b "BrandName" ``` ### CIP: Search Domains ```bash -python3 ~/.claude/skills/design/scripts/cip/search.py "business card letterhead" --domain deliverable -python3 ~/.claude/skills/design/scripts/cip/search.py "luxury premium elegant" --domain style -python3 ~/.claude/skills/design/scripts/cip/search.py "hospitality hotel" --domain industry -python3 ~/.claude/skills/design/scripts/cip/search.py "office reception" --domain mockup +python3 scripts/cip/search.py "business card letterhead" --domain deliverable +python3 scripts/cip/search.py "luxury premium elegant" --domain style +python3 scripts/cip/search.py "hospitality hotel" --domain industry +python3 scripts/cip/search.py "office reception" --domain mockup ``` ### CIP: Generate Mockups ```bash # With logo (RECOMMENDED) -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --deliverable "business card" --industry "consulting" +python3 scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --deliverable "business card" --industry "consulting" # Full CIP set -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --industry "consulting" --set +python3 scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --industry "consulting" --set # Pro model (4K text) -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo logo.png --deliverable "business card" --model pro +python3 scripts/cip/generate.py --brand "TopGroup" --logo logo.png --deliverable "business card" --model pro # Without logo -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TechFlow" --deliverable "business card" --no-logo-prompt +python3 scripts/cip/generate.py --brand "TechFlow" --deliverable "business card" --no-logo-prompt ``` Models: `flash` (default, `gemini-2.5-flash-image`), `pro` (`gemini-3-pro-image-preview`) @@ -109,7 +116,7 @@ Models: `flash` (default, `gemini-2.5-flash-image`), `pro` (`gemini-3-pro-image- ### CIP: Render HTML Presentation ```bash -python3 ~/.claude/skills/design/scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images /path/to/cip-output +python3 scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images /path/to/cip-output ``` **Tip:** If no logo exists, use Logo Design section above first. @@ -184,21 +191,21 @@ Load `references/banner-sizes-and-styles.md` for complete sizes and styles refer ### Icon: Generate Single Icon ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "settings gear" --style outlined -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "shopping cart" --style filled --color "#6366F1" -python3 ~/.claude/skills/design/scripts/icon/generate.py --name "dashboard" --category navigation --style duotone +python3 scripts/icon/generate.py --prompt "settings gear" --style outlined +python3 scripts/icon/generate.py --prompt "shopping cart" --style filled --color "#6366F1" +python3 scripts/icon/generate.py --name "dashboard" --category navigation --style duotone ``` ### Icon: Generate Batch Variations ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "cloud upload" --batch 4 --output-dir ./icons +python3 scripts/icon/generate.py --prompt "cloud upload" --batch 4 --output-dir ./icons ``` ### Icon: Multi-size Export ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "user profile" --sizes "16,24,32,48" --output-dir ./icons +python3 scripts/icon/generate.py --prompt "user profile" --sizes "16,24,32,48" --output-dir ./icons ``` ### Icon: Top Styles @@ -304,8 +311,20 @@ python3 --version || python --version ```bash export GEMINI_API_KEY="your-key" # https://aistudio.google.com/apikey pip install google-genai pillow + +# Optional MuAPI provider (no extra Python package required) +export MUAPI_API_KEY="your-key" ``` +MuAPI uses the asynchronous model endpoint and prediction result API. See the +[MuAPI API reference](https://muapi.ai/docs/api-reference) for authentication +and the [nano-banana model contract](https://api.muapi.ai/api/v1/models/nano-banana) +or [nano-banana-pro model contract](https://api.muapi.ai/api/v1/models/nano-banana-pro) +for the current model-specific schemas. The logo generator supports both documented +model slugs and sends their shared required `prompt` plus optional `aspect_ratio` +fields; the Pro model also accepts an optional `resolution` field that this focused +logo workflow leaves at the provider default. + > **Note for Windows:** Use `python` instead of `pip` where needed (e.g., `python -m pip install ...`). ## Integration diff --git a/ui-ux-pro-max/.claude/skills/design/references/cip-design.md b/ui-ux-pro-max/.claude/skills/design/references/cip-design.md index 81829ed5..40377690 100644 --- a/ui-ux-pro-max/.claude/skills/design/references/cip-design.md +++ b/ui-ux-pro-max/.claude/skills/design/references/cip-design.md @@ -16,49 +16,49 @@ Corporate Identity Program design with 50+ deliverables, 20 styles, 20 industrie ### CIP Brief (Start Here) ```bash -python3 ~/.claude/skills/design/scripts/cip/search.py "tech startup" --cip-brief -b "BrandName" +python3 scripts/cip/search.py "tech startup" --cip-brief -b "BrandName" ``` ### Search Domains ```bash # Deliverables -python3 ~/.claude/skills/design/scripts/cip/search.py "business card letterhead" --domain deliverable +python3 scripts/cip/search.py "business card letterhead" --domain deliverable # Design styles -python3 ~/.claude/skills/design/scripts/cip/search.py "luxury premium elegant" --domain style +python3 scripts/cip/search.py "luxury premium elegant" --domain style # Industry guidelines -python3 ~/.claude/skills/design/scripts/cip/search.py "hospitality hotel" --domain industry +python3 scripts/cip/search.py "hospitality hotel" --domain industry # Mockup contexts -python3 ~/.claude/skills/design/scripts/cip/search.py "office reception" --domain mockup +python3 scripts/cip/search.py "office reception" --domain mockup ``` ### Generate Mockups ```bash # With logo (RECOMMENDED - uses image editing) -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --deliverable "business card" --industry "consulting" +python3 scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --deliverable "business card" --industry "consulting" # Full CIP set with logo -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --industry "consulting" --set +python3 scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --industry "consulting" --set # Pro model for 4K text rendering -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo logo.png --deliverable "business card" --model pro +python3 scripts/cip/generate.py --brand "TopGroup" --logo logo.png --deliverable "business card" --model pro # Custom deliverables with aspect ratio -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "GreenLeaf" --logo logo.png --industry "organic food" --deliverables "letterhead,packaging,vehicle" --ratio 16:9 +python3 scripts/cip/generate.py --brand "GreenLeaf" --logo logo.png --industry "organic food" --deliverables "letterhead,packaging,vehicle" --ratio 16:9 # Without logo (AI generates interpretation) -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TechFlow" --deliverable "business card" --no-logo-prompt +python3 scripts/cip/generate.py --brand "TechFlow" --deliverable "business card" --no-logo-prompt ``` ### Render HTML Presentation ```bash -python3 ~/.claude/skills/design/scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images /path/to/cip-output -python3 ~/.claude/skills/design/scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images ./topgroup-cip --output presentation.html +python3 scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images /path/to/cip-output +python3 scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images ./topgroup-cip --output presentation.html ``` ## Models diff --git a/ui-ux-pro-max/.claude/skills/design/references/design-routing.md b/ui-ux-pro-max/.claude/skills/design/references/design-routing.md index 78f55874..4d745bd2 100644 --- a/ui-ux-pro-max/.claude/skills/design/references/design-routing.md +++ b/ui-ux-pro-max/.claude/skills/design/references/design-routing.md @@ -164,14 +164,14 @@ Application Code **Brand:** ```bash -node .claude/skills/brand/scripts/inject-brand-context.cjs -node .claude/skills/brand/scripts/validate-asset.cjs +node ../brand/scripts/inject-brand-context.cjs +node ../brand/scripts/validate-asset.cjs ``` **Tokens:** ```bash -node .claude/skills/design-system/scripts/generate-tokens.cjs -c tokens.json -node .claude/skills/design-system/scripts/validate-tokens.cjs -d src/ +node ../design-system/scripts/generate-tokens.cjs -c tokens.json +node ../design-system/scripts/validate-tokens.cjs -d src/ ``` **Components:** diff --git a/ui-ux-pro-max/.claude/skills/design/references/icon-design.md b/ui-ux-pro-max/.claude/skills/design/references/icon-design.md index db6db01d..961cdacc 100644 --- a/ui-ux-pro-max/.claude/skills/design/references/icon-design.md +++ b/ui-ux-pro-max/.claude/skills/design/references/icon-design.md @@ -13,29 +13,29 @@ AI-powered SVG icon generation using Gemini 3.1 Pro Preview. 15 styles, 12 categ ### Generate Single Icon ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "settings gear" --style outlined -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "shopping cart" --style filled --color "#6366F1" -python3 ~/.claude/skills/design/scripts/icon/generate.py --name "dashboard" --category navigation --style duotone +python3 scripts/icon/generate.py --prompt "settings gear" --style outlined +python3 scripts/icon/generate.py --prompt "shopping cart" --style filled --color "#6366F1" +python3 scripts/icon/generate.py --name "dashboard" --category navigation --style duotone ``` ### Generate Batch Variations ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "cloud upload" --batch 4 --output-dir ./icons -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "notification bell" --batch 6 --style outlined --output-dir ./icons +python3 scripts/icon/generate.py --prompt "cloud upload" --batch 4 --output-dir ./icons +python3 scripts/icon/generate.py --prompt "notification bell" --batch 6 --style outlined --output-dir ./icons ``` ### Generate Multiple Sizes ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "user profile" --sizes "16,24,32,48" --output-dir ./icons +python3 scripts/icon/generate.py --prompt "user profile" --sizes "16,24,32,48" --output-dir ./icons ``` ### List Styles/Categories ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --list-styles -python3 ~/.claude/skills/design/scripts/icon/generate.py --list-categories +python3 scripts/icon/generate.py --list-styles +python3 scripts/icon/generate.py --list-categories ``` ## CLI Options diff --git a/ui-ux-pro-max/.claude/skills/design/references/logo-design.md b/ui-ux-pro-max/.claude/skills/design/references/logo-design.md index 3fae93b1..258ae777 100644 --- a/ui-ux-pro-max/.claude/skills/design/references/logo-design.md +++ b/ui-ux-pro-max/.claude/skills/design/references/logo-design.md @@ -1,13 +1,13 @@ # Logo Design Reference -AI-powered logo design with 55+ styles, 30 color palettes, 25 industry guides. Gemini Nano Banana is the default provider; Atlas Cloud is also available as an explicit opt-in. +AI-powered logo design with 55+ styles, 30 color palettes, 25 industry guides. Gemini Nano Banana is the default provider; Atlas Cloud and MuAPI are also available as explicit opt-in providers. ## Scripts | Script | Purpose | |--------|---------| | `scripts/logo/search.py` | Search styles, colors, industries; generate design briefs | -| `scripts/logo/generate.py` | Generate logos with Gemini Nano Banana or Atlas Cloud | +| `scripts/logo/generate.py` | Generate logos with Gemini Nano Banana, Atlas Cloud, or MuAPI | | `scripts/logo/core.py` | BM25 search engine for logo data | ## Commands @@ -15,20 +15,20 @@ AI-powered logo design with 55+ styles, 30 color palettes, 25 industry guides. G ### Design Brief (Start Here) ```bash -python3 ~/.claude/skills/design/scripts/logo/search.py "tech startup modern" --design-brief -p "BrandName" +python3 scripts/logo/search.py "tech startup modern" --design-brief -p "BrandName" ``` ### Search Domains ```bash # Styles -python3 ~/.claude/skills/design/scripts/logo/search.py "minimalist clean" --domain style +python3 scripts/logo/search.py "minimalist clean" --domain style # Color palettes -python3 ~/.claude/skills/design/scripts/logo/search.py "tech professional" --domain color +python3 scripts/logo/search.py "tech professional" --domain color # Industry guidelines -python3 ~/.claude/skills/design/scripts/logo/search.py "healthcare medical" --domain industry +python3 scripts/logo/search.py "healthcare medical" --domain industry ``` ### Generate Logo @@ -36,12 +36,14 @@ python3 ~/.claude/skills/design/scripts/logo/search.py "healthcare medical" --do **ALWAYS** use white background for output logos. ```bash -python3 ~/.claude/skills/design/scripts/logo/generate.py --brand "TechFlow" --style minimalist --industry tech -python3 ~/.claude/skills/design/scripts/logo/generate.py --prompt "coffee shop vintage badge" --style vintage -python3 ~/.claude/skills/design/scripts/logo/generate.py --brand "TechFlow" --provider atlas +python3 scripts/logo/generate.py --brand "TechFlow" --style minimalist --industry tech +python3 scripts/logo/generate.py --prompt "coffee shop vintage badge" --style vintage +python3 scripts/logo/generate.py --brand "TechFlow" --provider atlas +python3 scripts/logo/generate.py --brand "TechFlow" --provider muapi +python3 scripts/logo/generate.py --brand "TechFlow" --provider muapi --muapi-model nano-banana-pro ``` -Options: `--style`, `--industry`, `--prompt`, `--provider`, `--atlas-model` +Options: `--style`, `--industry`, `--prompt`, `--provider`, `--atlas-model`, `--muapi-model` ## Available Styles @@ -93,4 +95,16 @@ pip install google-genai # Optional Atlas Cloud provider (no extra Python package required) export ATLASCLOUD_API_KEY="your-key" + +# Optional MuAPI provider (no extra Python package required) +export MUAPI_API_KEY="your-key" ``` + +MuAPI uses the asynchronous model endpoint and prediction result API. See the +[MuAPI API reference](https://muapi.ai/docs/api-reference) for authentication +and the [nano-banana model contract](https://api.muapi.ai/api/v1/models/nano-banana) +or [nano-banana-pro model contract](https://api.muapi.ai/api/v1/models/nano-banana-pro) +for the current model-specific schemas. The logo generator supports both documented +model slugs and sends their shared required `prompt` plus optional `aspect_ratio` +fields; the Pro model also accepts an optional `resolution` field that this focused +logo workflow leaves at the provider default. diff --git a/ui-ux-pro-max/.claude/skills/design/references/slides-copywriting-formulas.md b/ui-ux-pro-max/.claude/skills/design/references/slides-copywriting-formulas.md index ecf2875c..87352fc2 100644 --- a/ui-ux-pro-max/.claude/skills/design/references/slides-copywriting-formulas.md +++ b/ui-ux-pro-max/.claude/skills/design/references/slides-copywriting-formulas.md @@ -66,10 +66,10 @@ ```bash # Find formula for slide type -python .claude/skills/design-system/scripts/search-slides.py "problem agitation" -d copy +python ../design-system/scripts/search-slides.py "problem agitation" -d copy # Get emotion-appropriate formula -python .claude/skills/design-system/scripts/search-slides.py "urgency cta" -d copy +python ../design-system/scripts/search-slides.py "urgency cta" -d copy ``` ## Quick Reference diff --git a/ui-ux-pro-max/.claude/skills/design/references/slides-layout-patterns.md b/ui-ux-pro-max/.claude/skills/design/references/slides-layout-patterns.md index e2b3849f..0949ab0f 100644 --- a/ui-ux-pro-max/.claude/skills/design/references/slides-layout-patterns.md +++ b/ui-ux-pro-max/.claude/skills/design/references/slides-layout-patterns.md @@ -113,10 +113,10 @@ ```bash # Find layout for specific use -python .claude/skills/design-system/scripts/search-slides.py "metrics dashboard" -d layout +python ../design-system/scripts/search-slides.py "metrics dashboard" -d layout # Contextual recommendation -python .claude/skills/design-system/scripts/search-slides.py "traction slide" \ +python ../design-system/scripts/search-slides.py "traction slide" \ --context --position 4 --total 10 ``` diff --git a/ui-ux-pro-max/.claude/skills/design/references/slides-strategies.md b/ui-ux-pro-max/.claude/skills/design/references/slides-strategies.md index e004fe17..eae5b131 100644 --- a/ui-ux-pro-max/.claude/skills/design/references/slides-strategies.md +++ b/ui-ux-pro-max/.claude/skills/design/references/slides-strategies.md @@ -76,10 +76,10 @@ Pattern breaks at 1/3 and 2/3 positions create engagement peaks. ```bash # Find strategy by goal -python .claude/skills/design-system/scripts/search-slides.py "investor pitch" -d strategy +python ../design-system/scripts/search-slides.py "investor pitch" -d strategy # Get emotion arc -python .claude/skills/design-system/scripts/search-slides.py "series a funding" -d strategy --json +python ../design-system/scripts/search-slides.py "series a funding" -d strategy --json ``` ## Matching Strategy to Context diff --git a/ui-ux-pro-max/.claude/skills/design/scripts/cip/generate.py b/ui-ux-pro-max/.claude/skills/design/scripts/cip/generate.py index 0be632f5..e92b2e18 100644 --- a/ui-ux-pro-max/.claude/skills/design/scripts/cip/generate.py +++ b/ui-ux-pro-max/.claude/skills/design/scripts/cip/generate.py @@ -427,7 +427,10 @@ Image Editing Mode: action = check_logo_required(args.brand, skip_prompt=args.no_logo_prompt) if action == 'generate': print("\n💡 To generate a logo, use the logo-design skill:") - print(f" python ~/.claude/skills/design/scripts/logo/generate.py --brand \"{args.brand}\" --industry \"{args.industry}\"") + # Resolved from this file so the hint is correct from any cwd and in + # every install layout (plugin cache, project or --global install). + logo_script = Path(__file__).resolve().parents[1] / "logo" / "generate.py" + print(f" python \"{logo_script}\" --brand \"{args.brand}\" --industry \"{args.industry}\"") print("\n Then re-run this command with --logo ") sys.exit(0) elif action == 'exit': diff --git a/ui-ux-pro-max/.claude/skills/design/scripts/logo/generate.py b/ui-ux-pro-max/.claude/skills/design/scripts/logo/generate.py index 7de5b702..d006c130 100644 --- a/ui-ux-pro-max/.claude/skills/design/scripts/logo/generate.py +++ b/ui-ux-pro-max/.claude/skills/design/scripts/logo/generate.py @@ -1,12 +1,15 @@ #!/usr/bin/env python3 -"""Logo generation with Gemini or Atlas Cloud. +"""Logo generation with Gemini, Atlas Cloud, or MuAPI. Gemini remains the default provider. Atlas Cloud is opt-in with -``--provider atlas`` and uses its asynchronous image generation API. +``--provider atlas`` and uses its asynchronous image generation API. MuAPI is +opt-in with ``--provider muapi`` and uses its asynchronous image generation API +with the selected model's prompt/aspect-ratio contract. Models: - Nano Banana (default): gemini-2.5-flash-image - fast, high-volume, low-latency - Nano Banana Pro (--pro): gemini-3-pro-image-preview - professional quality, advanced reasoning +- MuAPI Nano Banana (--provider muapi): nano-banana - hosted asynchronous image generation Usage: python generate.py --prompt "tech startup logo minimalist blue" @@ -14,6 +17,8 @@ Usage: python generate.py --brand "TechFlow" --industry tech --style minimalist python generate.py --brand "TechFlow" --pro # Use Nano Banana Pro model python generate.py --brand "TechFlow" --provider atlas + python generate.py --brand "TechFlow" --provider muapi + python generate.py --brand "TechFlow" --provider muapi --muapi-model nano-banana-pro Batch mode (generates multiple variants): python generate.py --brand "Unikorn" --batch 9 --output-dir ./logos --pro @@ -57,6 +62,7 @@ load_env() # ============ CONFIGURATION ============ GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY") ATLASCLOUD_API_KEY = os.environ.get("ATLASCLOUD_API_KEY") +MUAPI_API_KEY = os.environ.get("MUAPI_API_KEY") # Gemini "Nano Banana" model configurations for image generation GEMINI_FLASH = "gemini-2.5-flash-image" # Nano Banana: fast, high-volume, low-latency @@ -65,9 +71,14 @@ GEMINI_PRO = "gemini-3-pro-image-preview" # Nano Banana Pro: professional quali # Atlas Cloud model validated against the live model catalog and schema. ATLAS_MODEL = "google/nano-banana-2-lite/text-to-image" ATLAS_API_BASE = "https://api.atlascloud.ai/api/v1" -HTTP_USER_AGENT = "ui-ux-pro-max/2.5 (Atlas Cloud logo provider)" +MUAPI_MODEL = "nano-banana" +MUAPI_MODELS = ("nano-banana", "nano-banana-pro") +MUAPI_API_BASE = "https://api.muapi.ai/api/v1" +HTTP_USER_AGENT = "ui-ux-pro-max/2.5 (logo generation)" ATLAS_POLL_INTERVAL = 2 ATLAS_MAX_POLLS = 90 +MUAPI_POLL_INTERVAL = 2 +MUAPI_MAX_POLLS = 90 # Supported aspect ratios ASPECT_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4"] @@ -156,13 +167,13 @@ def _validate_public_https_url(url): or parsed.username or parsed.password ): - raise ValueError("Atlas Cloud returned an invalid media URL") + raise ValueError("Provider returned an invalid media URL") hostname = parsed.hostname.lower().rstrip(".") if hostname == "localhost" or hostname.endswith( (".localhost", ".local", ".internal") ): - raise ValueError("Atlas Cloud media URL used a local hostname") + raise ValueError("Provider media URL used a local hostname") try: ip = ipaddress.ip_address(hostname) @@ -170,17 +181,26 @@ def _validate_public_https_url(url): return else: if not ip.is_global: - raise ValueError("Atlas Cloud media URL used a non-public address") + raise ValueError("Provider media URL used a non-public address") -def _json_request(url, api_key, method="GET", payload=None): +def _json_request( + url, api_key, method="GET", payload=None, api_key_header="Authorization" +): + if api_key_header == "Authorization": + auth_value = f"Bearer {api_key}" + elif api_key_header == "x-api-key": + auth_value = api_key + else: + raise ValueError("Unsupported API key header") + body = json.dumps(payload).encode("utf-8") if payload is not None else None request = Request( url, data=body, method=method, headers={ - "Authorization": f"Bearer {api_key}", + api_key_header: auth_value, "Accept": "application/json", "User-Agent": HTTP_USER_AGENT, **({"Content-Type": "application/json"} if body is not None else {}), @@ -192,10 +212,10 @@ def _json_request(url, api_key, method="GET", payload=None): except HTTPError as exc: detail = exc.read().decode("utf-8", errors="replace") raise RuntimeError( - f"Atlas Cloud request failed ({exc.code}): {detail[:300]}" + f"Provider request failed ({exc.code}): {detail[:300]}" ) from exc except (URLError, TimeoutError, json.JSONDecodeError) as exc: - raise RuntimeError(f"Atlas Cloud request failed: {exc}") from exc + raise RuntimeError(f"Provider request failed: {exc}") from exc def _atlas_prediction_data(response): @@ -210,6 +230,10 @@ def _atlas_prediction_data(response): def _download_atlas_image(url, output_path): + _download_image(url, output_path, "image provider") + + +def _download_image(url, output_path, provider_name): _validate_public_https_url(url) request = Request( url, @@ -222,14 +246,14 @@ def _download_atlas_image(url, output_path): content_type = response.headers.get_content_type() if not content_type.startswith("image/"): raise RuntimeError( - f"Atlas Cloud output is not an image ({content_type})" + f"{provider_name} output is not an image ({content_type})" ) image_data = response.read() except (HTTPError, URLError, TimeoutError) as exc: - raise RuntimeError(f"Unable to download Atlas Cloud image: {exc}") from exc + raise RuntimeError(f"Unable to download {provider_name} image: {exc}") from exc if not image_data: - raise RuntimeError("Atlas Cloud returned an empty image") + raise RuntimeError(f"{provider_name} returned an empty image") with open(output_path, "wb") as output_file: output_file.write(image_data) @@ -281,6 +305,132 @@ def _generate_with_atlas(prompt, output_path, aspect_ratio, api_key, model): raise RuntimeError("Atlas Cloud prediction timed out while polling") +def _muapi_response_objects(response): + """Return the response and common MuAPI envelopes without guessing fields.""" + if not isinstance(response, dict): + raise TypeError("MuAPI returned an invalid response") + + objects = [response] + for key in ("data", "output", "result"): + value = response.get(key) + if isinstance(value, dict) and value not in objects: + objects.append(value) + return objects + + +def _muapi_response_value(response, keys): + for item in _muapi_response_objects(response): + for key in keys: + value = item.get(key) + if value not in (None, ""): + return value + return None + + +def _muapi_error(response): + value = _muapi_response_value(response, ("error", "message", "detail")) + if isinstance(value, str): + return value[:300] + return "MuAPI request failed" + + +def _muapi_result_url(response): + """Return the documented result URL from the creation response.""" + for item in _muapi_response_objects(response): + urls = item.get("urls") + if not isinstance(urls, dict) or "get" not in urls: + continue + + result_url = urls.get("get") + if not isinstance(result_url, str) or not result_url: + raise RuntimeError( + "MuAPI creation response did not include a valid HTTPS result URL" + ) + try: + _validate_public_https_url(result_url) + except ValueError as exc: + raise RuntimeError( + "MuAPI creation response did not include a valid HTTPS result URL" + ) from exc + return result_url + + raise RuntimeError( + "MuAPI creation response did not include a valid HTTPS result URL" + ) + + +def _muapi_output_url(response): + for item in _muapi_response_objects(response): + outputs = item.get("outputs") + if isinstance(outputs, list): + for output in outputs: + if isinstance(output, str) and output.startswith("https://"): + return output + if isinstance(output, dict): + for key in ("url", "image_url"): + value = output.get(key) + if isinstance(value, str) and value.startswith("https://"): + return value + raise RuntimeError("MuAPI completed without an HTTPS image URL") + + +def _download_muapi_image(url, output_path): + _download_image(url, output_path, "MuAPI") + + +def _generate_with_muapi(prompt, output_path, aspect_ratio, api_key, model): + if not api_key: + raise RuntimeError("MUAPI_API_KEY not set") + if model not in MUAPI_MODELS: + raise RuntimeError( + f"Unsupported MuAPI logo model: {model}. " + f"Choose one of: {', '.join(MUAPI_MODELS)}" + ) + + payload = { + "prompt": prompt, + "aspect_ratio": aspect_ratio, + } + response = _json_request( + f"{MUAPI_API_BASE}/{model}", + api_key, + method="POST", + payload=payload, + api_key_header="x-api-key", + ) + request_id = _muapi_response_value(response, ("request_id", "id")) + if not isinstance(request_id, str) or not request_id: + raise RuntimeError("MuAPI did not return a request ID") + result_url = _muapi_result_url(response) + + data = response + for poll_number in range(MUAPI_MAX_POLLS + 1): + status = _muapi_response_value(data, ("status",)) + normalized_status = str(status or "").lower() + if normalized_status in {"completed", "succeeded", "success"}: + _download_muapi_image(_muapi_output_url(data), output_path) + return + if normalized_status in { + "failed", + "error", + "timeout", + "canceled", + "cancelled", + }: + raise RuntimeError(f"MuAPI generation {normalized_status}: {_muapi_error(data)}") + if poll_number == MUAPI_MAX_POLLS: + break + + time.sleep(MUAPI_POLL_INTERVAL) + data = _json_request( + result_url, + api_key, + api_key_header="x-api-key", + ) + + raise RuntimeError("MuAPI prediction timed out while polling") + + def _generate_with_gemini(prompt, output_path, aspect_ratio, use_pro): if not GEMINI_API_KEY: raise RuntimeError("GEMINI_API_KEY not set") @@ -344,8 +494,9 @@ def generate_logo( aspect_ratio=None, provider="gemini", atlas_model=ATLAS_MODEL, + muapi_model=MUAPI_MODEL, ): - """Generate a logo using Gemini or Atlas Cloud image generation. + """Generate a logo using Gemini, Atlas Cloud, or MuAPI image generation. Args: aspect_ratio: Image aspect ratio. Options: "1:1", "16:9", "9:16", "4:3", "3:4" @@ -365,6 +516,8 @@ def generate_logo( if provider == "atlas": model_label = f"Atlas Cloud ({atlas_model})" + elif provider == "muapi": + model_label = f"MuAPI ({muapi_model})" else: model_label = ( "Nano Banana Pro (gemini-3-pro-image-preview)" @@ -386,6 +539,14 @@ def generate_logo( ATLASCLOUD_API_KEY, atlas_model, ) + elif provider == "muapi": + _generate_with_muapi( + full_prompt, + output_path, + ratio, + MUAPI_API_KEY, + muapi_model, + ) else: _generate_with_gemini(full_prompt, output_path, ratio, use_pro) @@ -407,6 +568,7 @@ def generate_batch( aspect_ratio=None, provider="gemini", atlas_model=ATLAS_MODEL, + muapi_model=MUAPI_MODEL, ): """Generate multiple logo variants with different styles""" @@ -430,6 +592,8 @@ def generate_batch( model_label = ( f"Atlas Cloud ({atlas_model})" if provider == "atlas" + else f"MuAPI ({muapi_model})" + if provider == "muapi" else f"Nano Banana {'Pro' if use_pro else 'Flash'}" ) ratio = aspect_ratio if aspect_ratio in ASPECT_RATIOS else DEFAULT_ASPECT_RATIO @@ -466,6 +630,7 @@ def generate_batch( aspect_ratio=aspect_ratio, provider=provider, atlas_model=atlas_model, + muapi_model=muapi_model, ) if result: @@ -487,7 +652,7 @@ def generate_batch( def main(): parser = argparse.ArgumentParser( - description="Generate logos using Gemini or Atlas Cloud" + description="Generate logos using Gemini, Atlas Cloud, or MuAPI" ) parser.add_argument("--prompt", "-p", type=str, help="Logo description prompt") parser.add_argument("--brand", "-b", type=str, help="Brand name") @@ -514,7 +679,7 @@ def main(): ) parser.add_argument( "--provider", - choices=["gemini", "atlas"], + choices=["gemini", "atlas", "muapi"], default="gemini", help="Image provider (default: gemini)", ) @@ -523,6 +688,12 @@ def main(): default=ATLAS_MODEL, help=f"Atlas Cloud image model (default: {ATLAS_MODEL})", ) + parser.add_argument( + "--muapi-model", + choices=MUAPI_MODELS, + default=MUAPI_MODEL, + help=f"MuAPI image model (default: {MUAPI_MODEL})", + ) parser.add_argument( "--aspect-ratio", "-r", @@ -539,8 +710,11 @@ def main(): args = parser.parse_args() - if args.provider == "atlas" and args.pro: - parser.error("--pro is only available with --provider gemini") + if args.provider != "gemini" and args.pro: + parser.error( + "--pro is only available with --provider gemini; " + "use --muapi-model nano-banana-pro for MuAPI" + ) if args.list_styles: print("Available styles:") @@ -574,6 +748,7 @@ def main(): aspect_ratio=args.aspect_ratio, provider=args.provider, atlas_model=args.atlas_model, + muapi_model=args.muapi_model, ) else: generate_logo( @@ -586,6 +761,7 @@ def main(): aspect_ratio=args.aspect_ratio, provider=args.provider, atlas_model=args.atlas_model, + muapi_model=args.muapi_model, ) diff --git a/ui-ux-pro-max/.claude/skills/design/scripts/logo/tests/test_generate.py b/ui-ux-pro-max/.claude/skills/design/scripts/logo/tests/test_generate.py index 8420d90b..9c2addcd 100644 --- a/ui-ux-pro-max/.claude/skills/design/scripts/logo/tests/test_generate.py +++ b/ui-ux-pro-max/.claude/skills/design/scripts/logo/tests/test_generate.py @@ -128,5 +128,161 @@ class AtlasGenerationTests(unittest.TestCase): logo_generate._validate_public_https_url("https://assets.local/logo.png") +class MuapiGenerationTests(unittest.TestCase): + @patch.object(logo_generate, "_download_muapi_image") + @patch.object(logo_generate.time, "sleep") + @patch.object(logo_generate, "_json_request") + def test_muapi_submits_once_and_polls_until_completed( + self, json_request, sleep, download + ): + json_request.side_effect = [ + { + "id": "req-123", + "status": "created", + "output": { + "urls": { + "get": "https://api.muapi.ai/api/v1/results/req-123" + } + }, + }, + {"id": "req-123", "status": "processing"}, + { + "id": "req-123", + "status": "completed", + "output": {"outputs": ["https://media.example.com/logo.png"]}, + }, + ] + + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + self.assertEqual(json_request.call_count, 3) + self.assertEqual( + json_request.call_args_list[0], + call( + f"{logo_generate.MUAPI_API_BASE}/nano-banana", + "muapi-key", + method="POST", + payload={"prompt": "logo prompt", "aspect_ratio": "1:1"}, + api_key_header="x-api-key", + ), + ) + self.assertEqual( + json_request.call_args_list[1:], + [ + call( + "https://api.muapi.ai/api/v1/results/req-123", + "muapi-key", + api_key_header="x-api-key", + ), + call( + "https://api.muapi.ai/api/v1/results/req-123", + "muapi-key", + api_key_header="x-api-key", + ), + ], + ) + self.assertEqual(sleep.call_count, 2) + download.assert_called_once_with( + "https://media.example.com/logo.png", "logo.png" + ) + + @patch.object(logo_generate, "_json_request") + def test_muapi_does_not_retry_generation_post(self, json_request): + json_request.side_effect = RuntimeError("network error") + + with self.assertRaisesRegex(RuntimeError, "network error"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + json_request.assert_called_once() + + def test_muapi_requires_key_and_known_model(self): + with self.assertRaisesRegex(RuntimeError, "MUAPI_API_KEY not set"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", None, "nano-banana" + ) + + with self.assertRaisesRegex(RuntimeError, "Unsupported MuAPI logo model"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "unknown-model" + ) + + @patch.object(logo_generate, "build_opener") + def test_muapi_uses_x_api_key_header(self, build_opener): + class Response: + def __enter__(self): + return self + + def __exit__(self, *args): + return None + + @staticmethod + def read(): + return b"{}" + + build_opener.return_value.open.return_value = Response() + + logo_generate._json_request( + "https://api.muapi.ai/api/v1/nano-banana", + "muapi-key", + method="POST", + payload={"prompt": "logo"}, + api_key_header="x-api-key", + ) + + request = build_opener.return_value.open.call_args.args[0] + headers = {key.lower(): value for key, value in request.header_items()} + self.assertEqual(headers["x-api-key"], "muapi-key") + self.assertNotIn("authorization", headers) + + @patch.object(logo_generate, "_json_request") + def test_muapi_reports_failed_prediction(self, json_request): + json_request.side_effect = [ + { + "request_id": "req-123", + "output": { + "urls": { + "get": "https://api.muapi.ai/api/v1/results/req-123" + } + }, + }, + {"status": "failed", "error": "invalid prompt"}, + ] + + with self.assertRaisesRegex(RuntimeError, "invalid prompt"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + @patch.object(logo_generate, "_json_request") + def test_muapi_requires_creation_result_url(self, json_request): + json_request.return_value = {"request_id": "req-123", "status": "created"} + + with self.assertRaisesRegex(RuntimeError, "valid HTTPS result URL"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + json_request.assert_called_once() + + @patch.object(logo_generate, "_json_request") + def test_muapi_rejects_invalid_creation_result_url(self, json_request): + json_request.return_value = { + "request_id": "req-123", + "status": "created", + "output": {"urls": {"get": "http://api.muapi.ai/results/req-123"}}, + } + + with self.assertRaisesRegex(RuntimeError, "valid HTTPS result URL"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + json_request.assert_called_once() + + if __name__ == "__main__": unittest.main() diff --git a/ui-ux-pro-max/.claude/skills/slides/SKILL.md b/ui-ux-pro-max/.claude/skills/slides/SKILL.md index 38750ff1..52d345d1 100644 --- a/ui-ux-pro-max/.claude/skills/slides/SKILL.md +++ b/ui-ux-pro-max/.claude/skills/slides/SKILL.md @@ -24,6 +24,10 @@ Strategic HTML presentation design with data visualization. |------------|-------------|-----------| | `create` | Create strategic presentation slides | `references/create.md` | +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## References (Knowledge Base) | Topic | File | diff --git a/ui-ux-pro-max/.claude/skills/slides/references/copywriting-formulas.md b/ui-ux-pro-max/.claude/skills/slides/references/copywriting-formulas.md index ecf2875c..87352fc2 100644 --- a/ui-ux-pro-max/.claude/skills/slides/references/copywriting-formulas.md +++ b/ui-ux-pro-max/.claude/skills/slides/references/copywriting-formulas.md @@ -66,10 +66,10 @@ ```bash # Find formula for slide type -python .claude/skills/design-system/scripts/search-slides.py "problem agitation" -d copy +python ../design-system/scripts/search-slides.py "problem agitation" -d copy # Get emotion-appropriate formula -python .claude/skills/design-system/scripts/search-slides.py "urgency cta" -d copy +python ../design-system/scripts/search-slides.py "urgency cta" -d copy ``` ## Quick Reference diff --git a/ui-ux-pro-max/.claude/skills/slides/references/layout-patterns.md b/ui-ux-pro-max/.claude/skills/slides/references/layout-patterns.md index e2b3849f..0949ab0f 100644 --- a/ui-ux-pro-max/.claude/skills/slides/references/layout-patterns.md +++ b/ui-ux-pro-max/.claude/skills/slides/references/layout-patterns.md @@ -113,10 +113,10 @@ ```bash # Find layout for specific use -python .claude/skills/design-system/scripts/search-slides.py "metrics dashboard" -d layout +python ../design-system/scripts/search-slides.py "metrics dashboard" -d layout # Contextual recommendation -python .claude/skills/design-system/scripts/search-slides.py "traction slide" \ +python ../design-system/scripts/search-slides.py "traction slide" \ --context --position 4 --total 10 ``` diff --git a/ui-ux-pro-max/.claude/skills/slides/references/slide-strategies.md b/ui-ux-pro-max/.claude/skills/slides/references/slide-strategies.md index e004fe17..eae5b131 100644 --- a/ui-ux-pro-max/.claude/skills/slides/references/slide-strategies.md +++ b/ui-ux-pro-max/.claude/skills/slides/references/slide-strategies.md @@ -76,10 +76,10 @@ Pattern breaks at 1/3 and 2/3 positions create engagement peaks. ```bash # Find strategy by goal -python .claude/skills/design-system/scripts/search-slides.py "investor pitch" -d strategy +python ../design-system/scripts/search-slides.py "investor pitch" -d strategy # Get emotion arc -python .claude/skills/design-system/scripts/search-slides.py "series a funding" -d strategy --json +python ../design-system/scripts/search-slides.py "series a funding" -d strategy --json ``` ## Matching Strategy to Context diff --git a/ui-ux-pro-max/.claude/skills/ui-styling/SKILL.md b/ui-ux-pro-max/.claude/skills/ui-styling/SKILL.md index 5824efee..3f86abc8 100644 --- a/ui-ux-pro-max/.claude/skills/ui-styling/SKILL.md +++ b/ui-ux-pro-max/.claude/skills/ui-styling/SKILL.md @@ -53,6 +53,10 @@ Use when: - Minimal text, maximum visual impact - Systematic patterns and refined aesthetics +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## Quick Start ### Component + Styling Setup diff --git a/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/data/catalog-summary.json b/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/data/catalog-summary.json index 4a8c7643..0728f827 100644 --- a/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/data/catalog-summary.json +++ b/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/data/catalog-summary.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "verifiedAt": "2026-08-13", + "verifiedAt": "2026-08-26", "counts": { "styles": { "total": 88, diff --git a/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/tests/test_catalog_summary_line_endings.py b/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/tests/test_catalog_summary_line_endings.py new file mode 100644 index 00000000..056c8e5e --- /dev/null +++ b/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/tests/test_catalog_summary_line_endings.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""The catalog snapshot must not depend on the checkout's line endings. + +Regression test for bd19ab9 (#462), where catalog-summary.json was regenerated +on a CRLF checkout. Every recorded sha256 was the CRLF hash of the source file, +so `verify:data` failed on every LF platform, including CI. +""" + +import hashlib +import importlib.util +import json +import shutil +import tempfile +import unittest +from pathlib import Path + +REPO = next( + parent for parent in Path(__file__).resolve().parents + if (parent / "scripts" / "generate-catalog-summary.py").is_file() +) +DATA = REPO / "src/ui-ux-pro-max/data" +SNAPSHOT_FILES = ( + "google-fonts.csv", + "google-font-licenses.json", + "icons.csv", + "phosphor-icons-upstream.json", +) + + +def _load_generator(): + path = REPO / "scripts" / "generate-catalog-summary.py" + spec = importlib.util.spec_from_file_location("generate_catalog_summary", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class CatalogSummaryLineEndingsTest(unittest.TestCase): + def test_digest_is_identical_for_lf_and_crlf(self): + digest = _load_generator().digest + with tempfile.TemporaryDirectory() as tmp: + lf = Path(tmp) / "lf.csv" + crlf = Path(tmp) / "crlf.csv" + lf.write_bytes(b"id,name\n1,alpha\n2,beta\n") + crlf.write_bytes(b"id,name\r\n1,alpha\r\n2,beta\r\n") + self.assertEqual( + digest(lf), digest(crlf), + "snapshot hashes must not change with the checkout's line endings", + ) + + def test_committed_snapshot_matches_normalized_sources(self): + summary = json.loads((DATA / "catalog-summary.json").read_text(encoding="utf-8")) + for name in SNAPSHOT_FILES: + expected = hashlib.sha256( + (DATA / name).read_bytes().replace(b"\r\n", b"\n") + ).hexdigest() + self.assertEqual( + summary["snapshots"][name]["sha256"], expected, + f"{name}: committed snapshot hash does not match the LF-normalized source", + ) + + def test_crlf_checkout_produces_the_committed_hashes(self): + """Simulate a Windows checkout: the recorded hashes must still validate.""" + digest = _load_generator().digest + summary = json.loads((DATA / "catalog-summary.json").read_text(encoding="utf-8")) + with tempfile.TemporaryDirectory() as tmp: + for name in SNAPSHOT_FILES: + crlf_copy = Path(tmp) / name + raw = (DATA / name).read_bytes().replace(b"\r\n", b"\n") + crlf_copy.write_bytes(raw.replace(b"\n", b"\r\n")) + self.assertEqual( + digest(crlf_copy), summary["snapshots"][name]["sha256"], + f"{name}: a CRLF checkout would record a different hash", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py b/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py new file mode 100644 index 00000000..36514964 --- /dev/null +++ b/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py @@ -0,0 +1,82 @@ +"""Every script invocation in the shipped skill markdown resolves from the skill directory. + +Regression test for #474. The sub-skills ship in two copies (.claude/skills// +for the plugin, cli/assets/skills// for CLI installs) and land in layouts where +neither the project root nor ~/.claude/skills/ is a valid anchor: the plugin cache, a +project's .claude/skills/, ~/.claude/skills/ (--global), or a manual copy. The one anchor +that exists in all of them is the skill's own directory, so documented commands use +`scripts/` for the skill's own scripts and `..//scripts/` for a +sibling sub-skill (the sub-skills are always installed side by side). + +This test extracts every `python|python3|node|bash ` invocation from every +markdown file under both trees and asserts that the path is skill-relative and names a +file that ships. The core skill's `${CLAUDE_PLUGIN_ROOT}/.claude/skills/...` form is +resolved against the repository root, which is what that variable denotes under a +plugin install - and accepted only in that file, because the sub-skills also ship +through the CLI, where the variable does not exist. The grep-based path contract in check-asset-sync.yml is the negative +side (no home-, project- or variable-rooted paths anywhere, code included); this is +the positive side (every documented invocation points at a real file). +""" + +import re +import unittest +from pathlib import Path + +REPO = next( + parent for parent in Path(__file__).resolve().parents + if (parent / "scripts" / "generate-catalog-summary.py").is_file() +) +SKILL_TREES = ("cli/assets/skills", ".claude/skills") +# The only file that may use the plugin-root form: hand-authored for the plugin install +# and not shipped by the CLI (sync-assets.mjs mirrors data/ and scripts/, never SKILL.md). +# (Built from segments: the path contract in check-asset-sync.yml scans this file too.) +PLUGIN_ONLY_FILE = Path(".claude") / "skills" / "ui-ux-pro-max" / "SKILL.md" +INVOCATION = re.compile(r'(? 3 and parts[2] == "scripts" and (skill_dir.parent / parts[1]).is_dir(): + return skill_dir.parent / parts[1] / "/".join(parts[2:]), None + return None, "a sibling invocation must be ..//scripts/ and the sibling must ship" + return None, "not skill-relative (expected scripts/ or ..//scripts/)" + + +class SkillScriptPathsTest(unittest.TestCase): + def test_every_shipped_markdown_invocation_resolves_from_the_skill_directory(self): + problems, seen = [], 0 + for skill_dir, md, lineno, path in shipped_invocations(): + seen += 1 + target, reason = resolve(skill_dir, md, path) + if reason is None and not target.is_file(): + reason = f"no such file: {target}" + if reason: + problems.append(f"{md.relative_to(REPO)}:{lineno}: {path} -- {reason}") + # Guard against a silently broken extractor: the two trees carry well over + # a hundred documented invocations between them. + self.assertGreater(seen, 100, f"extractor found only {seen} invocations") + self.assertEqual(problems, [], "\n" + "\n".join(problems)) + + +if __name__ == "__main__": + unittest.main() diff --git a/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/validate_data.py b/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/validate_data.py index c7797cd9..8b6c7941 100644 --- a/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/validate_data.py +++ b/ui-ux-pro-max/.claude/skills/ui-ux-pro-max/scripts/validate_data.py @@ -663,7 +663,11 @@ def _check_catalog_summary(summary, licenses, phosphor, problems): problems.append(f"[catalog:summary] stale count for {key}") snapshots = summary.get("snapshots") if isinstance(summary.get("snapshots"), dict) else {} for name in ("google-fonts.csv", "google-font-licenses.json", "icons.csv", "phosphor-icons-upstream.json"): - digest = hashlib.sha256((DATA_DIR / name).read_bytes()).hexdigest() + # Line endings are normalized so the check matches + # generate-catalog-summary.py on CRLF checkouts too. + digest = hashlib.sha256( + (DATA_DIR / name).read_bytes().replace(b"\r\n", b"\n") + ).hexdigest() if snapshots.get(name) != {"sha256": digest}: problems.append(f"[catalog:summary] stale snapshot for {name}") policy = summary.get("promotionPolicy") diff --git a/ui-ux-pro-max/.gitattributes b/ui-ux-pro-max/.gitattributes new file mode 100644 index 00000000..77b40e69 --- /dev/null +++ b/ui-ux-pro-max/.gitattributes @@ -0,0 +1,6 @@ +# catalog-summary.json records sha256 hashes of the files below, computed over +# their bytes. A CRLF checkout therefore produces different hashes than an LF +# one, which makes `verify:data` fail on every other platform (see #462/#478). +# Pin these files to LF so the snapshot is reproducible everywhere. +src/ui-ux-pro-max/data/*.csv text eol=lf +src/ui-ux-pro-max/data/*.json text eol=lf diff --git a/ui-ux-pro-max/.github/workflows/check-asset-sync.yml b/ui-ux-pro-max/.github/workflows/check-asset-sync.yml index 578f9cf5..8c80a430 100644 --- a/ui-ux-pro-max/.github/workflows/check-asset-sync.yml +++ b/ui-ux-pro-max/.github/workflows/check-asset-sync.yml @@ -5,8 +5,7 @@ on: paths: - "src/ui-ux-pro-max/**" - "cli/assets/**" - - ".claude/skills/ui-ux-pro-max/data/**" - - ".claude/skills/ui-ux-pro-max/scripts/**" + - ".claude/skills/**" - "cli/scripts/sync-assets.mjs" - "cli/package.json" - "scripts/evaluate-relevance.py" @@ -18,8 +17,9 @@ on: - "src/ui-ux-pro-max/**" - "cli/assets/**" - "cli/package.json" - - ".claude/skills/ui-ux-pro-max/data/**" - - ".claude/skills/ui-ux-pro-max/scripts/**" + - ".claude/skills/**" + - "cli/scripts/sync-assets.mjs" + - ".github/workflows/check-asset-sync.yml" jobs: check-assets: @@ -38,3 +38,58 @@ jobs: # installed as a plugin, and previously had no sync check at all. - name: Check assets are in sync with source of truth run: npm --prefix cli run check:assets + # Path contract (#474): skill instructions and scripts reach their scripts + # via skill-relative paths ("scripts/" for the skill's own, "..//scripts/" + # for a sibling sub-skill) so they resolve in every install context: marketplace/ + # plugin cache, project-level CLI install, CLI --global install, manual copy. + # Home-rooted ("~/.claude/skills/"), project-rooted (".claude/skills/") + # and variable-rooted ("$HOME/...", "${PWD}/...") forms each work in only one of them. + # Every file under both skill trees is checked, not just SKILL.md - the first + # version of this step looked only at SKILL.md and missed 27 home-rooted paths + # one directory down in references/. The one allowed absolute form is + # "${CLAUDE_PLUGIN_ROOT}/.claude/skills/ui-ux-pro-max/..." (braced or bare variable, + # directly followed by "/"): the core skill's SKILL.md is hand-authored for the + # plugin install only and that variable anchors it there. The same variable into a + # sub-skill is flagged, because sub-skills are also installed by the CLI where it is + # unset - and a third check pins the token itself to that one file (plus the checker + # that names it), so a sub-skill cannot borrow the core form either: sub-skills ship + # through the CLI too, where the variable does not exist. Both patterns require a path INTO a named skill ("skills/"), so a bare + # mention of the directory in prose or a code comment ("~/.claude/skills/, or ...") + # is not a hit - naming a skill after "skills/" in prose is. + # LC_ALL=C so that only NUL-containing files count as binary and an offending line + # with a stray non-UTF-8 byte is printed instead of suppressed as improperly encoded + # (the verdict is the same in both locales; the diagnostic is not); -I then skips + # .claude/skills/ui-styling/scripts/.coverage, a tracked SQLite database whose + # recorded absolute paths contain "/.claude/skills/ui-styling/". + # Not covered: backslash-separated Windows spellings and the platform-root-relative + # "skills//..." form - the docs are bash-fenced and skill-relative, so neither + # appears; the positive side (every documented invocation names a file that ships) + # is src/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py. + # grep exit codes: 0 = hits (violation), 1 = clean, 2 = error - only 1 passes, so an + # unreadable file can never turn into a green run (a missing tree is caught above). + - name: Path contract - no install-specific skill paths + run: | + for d in .claude/skills cli/assets/skills; do + [ -d "$d" ] || { echo "::error::$d is missing - the path contract has nothing to scan"; exit 1; } + done + status=0 + rc=0; LC_ALL=C grep -rnIP '~/\.claude/skills/[A-Za-z0-9_-]+\b' .claude/skills cli/assets/skills || rc=$? + if [ "$rc" -ne 1 ]; then + echo "::error::home-rooted skill paths (~/.claude/skills/) only resolve for one install layout - use skill-relative paths (see #474); grep rc=$rc" + status=1 + fi + rc=0; LC_ALL=C grep -rnIP '(?, \$HOME/..., \${CLAUDE_PLUGIN_ROOT}/... outside the core skill) only resolve for one install layout - use skill-relative paths (see #474); grep rc=$rc" + status=1 + fi + rc=0; found=$(LC_ALL=C grep -rlIF 'CLAUDE_PLUGIN_ROOT' .claude/skills cli/assets/skills) || rc=$? + if [ "$rc" -eq 2 ]; then echo "::error::grep failed while scanning for CLAUDE_PLUGIN_ROOT (rc=2)"; status=1; fi + offenders=$(printf '%s\n' "$found" | grep -vxF -e '.claude/skills/ui-ux-pro-max/SKILL.md' -e '.claude/skills/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py' | grep -v '^$' || true) + if [ -n "$offenders" ]; then + printf '%s\n' "$offenders" + echo "::error::CLAUDE_PLUGIN_ROOT is only defined under a plugin install; only the plugin-only core SKILL.md may use it - sub-skills ship through the CLI too (see #474)" + status=1 + fi + if [ "$status" -eq 0 ]; then echo "OK: all skill paths are skill-relative"; fi + exit "$status" diff --git a/ui-ux-pro-max/README.id.md b/ui-ux-pro-max/README.id.md new file mode 100644 index 00000000..0ad4b7ac --- /dev/null +++ b/ui-ux-pro-max/README.id.md @@ -0,0 +1,663 @@ +# [UI UX Pro Max](https://uupm.cc) + +

+ 🇮🇩 Bahasa Indonesia | + 🇰🇷 한국어 | + 🇻🇳 Tiếng Việt | + 🇨🇳 简体中文 | + 🇺🇸 English +

+ +

+ Rilis GitHub + 192 aturan penalaran + 79 gaya UI yang dapat dicari + Python 3.x + Lisensi +

+ +

+ npm + unduhan npm + bintang GitHub + PayPal +

+ +Skill AI yang menyediakan kecerdasan desain untuk membangun UI/UX profesional di berbagai platform dan framework. + +

+ + UI UX Pro Max + +

+ +

+ Jika proyek ini bermanfaat bagi Anda, pertimbangkan untuk mendukungnya:

+ Donasi PayPal +

+ +

+ Proyek lainnya
+ NextLevelBuilder.io | GoClaw.sh | ClaudeKit.cc | TOSE.sh +

+ +## Yang Baru di v2.0 + +### Pembuatan Design System Cerdas + +Fitur unggulan v2.0 adalah **Design System Generator** — mesin penalaran berbasis AI yang menganalisis kebutuhan proyek Anda dan menghasilkan design system yang lengkap serta disesuaikan dalam hitungan detik. + +``` ++----------------------------------------------------------------------------------------+ +| TARGET: Serenity Spa - DESIGN SYSTEM YANG DIREKOMENDASIKAN | ++----------------------------------------------------------------------------------------+ +| | +| POLA: Hero-Centric + Social Proof | +| Konversi: Didorong emosi dengan elemen kepercayaan | +| CTA: Di atas fold, diulang setelah testimonial | +| Bagian: | +| 1. Hero | +| 2. Layanan | +| 3. Testimonial | +| 4. Booking | +| 5. Kontak | +| | +| GAYA: Soft UI Evolution | +| Kata kunci: Bayangan lembut, kedalaman halus, tenang, premium, bentuk organik | +| Cocok untuk: Wellness, kecantikan, brand lifestyle, layanan premium | +| Performa: cost:low | Aksesibilitas: risk:conditional; verifikasi kebutuhan | +| | +| WARNA: | +| Primer: #E8B4B8 (Soft Pink) | +| Sekunder: #A8D5BA (Sage Green) | +| CTA: #D4AF37 (Gold) | +| Background: #FFF5F5 (Warm White) | +| Teks: #2D3436 (Charcoal) | +| Catatan: Palet menenangkan dengan aksen emas untuk nuansa mewah | +| | +| TIPOGRAFI: Cormorant Garamond / Montserrat | +| Nuansa: Elegan, menenangkan, sophisticated | +| Cocok untuk: Brand mewah, wellness, kecantikan, editorial | +| Google Fonts: https://fonts.google.com/share?selection.family=... | +| | +| EFEK UTAMA: | +| Bayangan lembut + Transisi sesuai konteks + Hover state yang halus | +| | +| HINDARI (Anti-pattern): | +| Warna neon terang + Animasi keras + Dark mode + Gradien ungu/merah muda ala AI | +| | +| CHECKLIST PRA-DELIVERY: | +| [ ] Tidak menggunakan emoji sebagai ikon (gunakan SVG: Heroicons/Lucide) | +| [ ] cursor-pointer pada semua elemen yang dapat diklik | +| [ ] Timing interaksi mengikuti platform, komponen, dan preferensi pengguna | +| [ ] Light mode: kontras teks minimum 4.5:1 | +| [ ] Focus state terlihat untuk navigasi keyboard | +| [ ] Menghormati prefers-reduced-motion | +| [ ] Teks, chip, dan badge reflow tanpa terpotong atau merusak label | +| [ ] Responsive: 375px, 768px, 1024px, 1440px | +| | ++----------------------------------------------------------------------------------------+ +``` + +### Cara Kerja Pembuatan Design System + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 1. PERMINTAAN PENGGUNA │ +│ "Buat landing page untuk spa kecantikan saya" │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ 2. PENCARIAN MULTI-DOMAIN (5 pencarian paralel) │ +│ • Pencocokan jenis produk (192 kategori) │ +│ • Rekomendasi gaya (79 dapat dicari; 50 aktif) │ +│ • Pemilihan palet warna (192 palet) │ +│ • Pola landing page (34 pola) │ +│ • Pasangan tipografi (74 kombinasi font) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ 3. MESIN PENALARAN │ +│ • Cocokkan produk → aturan kategori UI │ +│ • Terapkan prioritas gaya (ranking BM25) │ +│ • Filter anti-pattern untuk industri │ +│ • Proses aturan keputusan (kondisi JSON) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ 4. OUTPUT DESIGN SYSTEM LENGKAP │ +│ Pola + Gaya + Warna + Tipografi + Efek │ +│ + Anti-pattern yang dihindari + Checklist pra-delivery │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 192 Aturan Penalaran Khusus Industri + +Mesin penalaran ini mencakup aturan khusus untuk: + +| Kategori | Contoh | +|----------|--------| +| **Teknologi & SaaS** | SaaS, Micro SaaS, layanan B2B, Developer Tool / IDE, platform AI/chatbot, platform keamanan siber | +| **Keuangan** | Fintech/Crypto, perbankan, asuransi, pelacak keuangan pribadi, alat invoice & billing | +| **Kesehatan** | Klinik medis, apotek, kedokteran gigi, veteriner, kesehatan mental, pengingat obat | +| **E-commerce** | Umum, mewah, marketplace (P2P), subscription box, pengantaran makanan | +| **Layanan** | Kecantikan/spa, restoran, hotel, hukum, layanan rumah, booking & appointment | +| **Kreatif** | Portofolio, agensi, fotografi, gaming, streaming musik, editor foto/video | +| **Gaya Hidup** | Pelacak kebiasaan, resep & memasak, meditasi, cuaca, diari, pelacak suasana hati | +| **Teknologi Baru** | Web3/NFT, Spatial Computing, Quantum Computing, armada drone otonom | + +Setiap aturan mencakup: +- **Pola yang Direkomendasikan** - Struktur landing page +- **Prioritas Gaya** - Gaya UI yang paling cocok +- **Nuansa Warna** - Palet yang sesuai dengan industri +- **Nuansa Tipografi** - Pencocokan karakter font +- **Efek Utama** - Animasi dan interaksi +- **Anti-Pattern** - Hal yang TIDAK boleh dilakukan (misalnya, "AI purple/pink gradients" untuk perbankan) + +## Fitur + +- **79 Gaya UI yang Dapat Dicari (50 aktif)** - Glassmorphism, Claymorphism, Minimalism, Brutalism, Neumorphism, Bento Grid, Dark Mode, AI-Native UI, dan lainnya +- **192 Palet Warna** - Palet khusus industri yang selaras 1:1 dengan 192 jenis produk +- **74 Pasangan Font** - Kombinasi tipografi pilihan dengan import Google Fonts +- **25 Jenis Chart** - Rekomendasi untuk dashboard dan analitik +- **22 Tech Stack** - React, Next.js, Astro, Vue, Nuxt.js, Nuxt UI, Svelte, SwiftUI, React Native, Flutter, HTML+Tailwind, shadcn/ui, Jetpack Compose, Angular, Laravel, Three.js, JavaFX, WPF, WinUI 3, UWP, Avalonia, Uno Platform +- **119 Panduan UX** - Best practice, anti-pattern, aturan aksesibilitas, layout teks yang tangguh, label ringkas, dan interaksi yang dapat dibatalkan +- **192 Aturan Penalaran** - Pembuatan design system khusus industri (BARU di v2.0) + +### Teks Tangguh dan UI Ringkas + +Panduan ini kini mencakup kegagalan umum di lingkungan production terkait heading, token panjang, +chip, badge, dan micro-interaction yang terinterupsi: + +- Pembungkusan heading yang seimbang adalah progressive enhancement, bukan jaminan bahwa + kata tertentu akan tetap berada pada baris terakhir. Desain tetap harus berfungsi dengan + pembungkusan alami di berbagai lebar, font, dan locale. +- Teks penting harus dapat mengalir ulang tanpa terpotong pada lebar sempit, zoom browser, text + scaling, dan override spasi pengguna. URL dan identifier panjang boleh dibungkus dengan aman. +- Kumpulan chip dan tag sebaiknya membungkus atau menggunakan disclosure `+n` yang dapat dioperasikan. Label ringkas + sebaiknya tetap utuh jika memungkinkan; pemotongan yang tidak dapat dihindari memerlukan jalur + nilai penuh yang aksesibel bagi pengguna keyboard, pointer, dan sentuhan. +- Makna badge tidak boleh bergantung pada warna saja. Chip interaktif memerlukan semantik native, + focus yang terlihat, dan state yang dapat diprogram; live count memerlukan konteks yang bermakna. +- Interaksi cepat dapat membatalkan animasi, tetapi state semantik akhir, focus, dan + konten harus tetap benar. Timing dipilih sesuai platform dan komponen, + dengan tetap menghormati preferensi reduced-motion. + +### Taksonomi Gaya + +Katalog berisi **79 gaya yang dapat dicari** dengan dukungan ID dan alias yang stabil: + +| Status | Jumlah | Perilaku pencarian | +|--------|-------:|--------------------| +| Aktif | 50 | Disertakan dalam rekomendasi normal dan ditampilkan secara default di gallery | +| Tambahan | 29 | Dikembalikan untuk intent varian/sistem yang eksplisit atau exact; tersedia melalui filter status gallery | +| Deprecated | 9 | Dikecualikan dari ranking normal; nama legacy dialihkan ke gaya canonical atau landing pattern | + +Set aktif mencakup 43 keluarga visual umum, 2 gaya khusus mobile, 3 platform/design system resmi, 1 material platform, dan 1 gaya analitik inti. Sistem resmi saat ini mencakup Fluent 2, Shopify Polaris, dan Adobe Spectrum; Liquid Glass dikategorikan sebagai material platform Apple, Material 3 Expressive tetap menjadi varian Material mobile, dan Spectrum 2 bersifat tambahan. Struktur landing page berada dalam dataset terpisah yang berisi 34 pola landing, sehingga tidak bersaing dengan gaya visual dalam ranking BM25. + +Lihat [`styles.csv`](src/ui-ux-pro-max/data/styles.csv) untuk taksonomi lengkap dan metadata yang menyertakan provenance. + +## 💎 Perbandingan Versi Basic vs. Premium + +Banyak pengguna bertanya mengenai perbedaan antara versi open-source dan premium. Berikut rincian untuk membantu Anda memilih yang paling sesuai dengan workflow Anda. + +### 🟢 Versi Basic (Repository Ini) +* **Sepenuhnya Open Source:** Cocok untuk developer individu, hobbyist, dan proyek standar. +* **Kecerdasan UI/UX Inti:** Akses penuh ke 79 gaya UI yang dapat dicari (50 aktif), 192 jenis produk, palet warna, dan pasangan font pilihan. +* **Rekomendasi Cerdas:** Mesin pencarian BM25 bawaan untuk pencocokan desain yang sangat akurat. +* **Dukungan Cross-Platform:** Panduan khusus stack yang mendukung 22 framework utama (React, Vue, Tailwind, iOS, Android, dll.). +* **Pembuatan Design System:** Buat aturan UI, pola, dan logika yang disesuaikan secara instan melalui CLI. + +### 🟡 Versi Premium +* **Skill Brand Design yang Diperluas:** Melampaui UI/UX dengan mencakup pembuatan Brand Identity, Logo Design, Corporate Identity Programs (CIP), Banner, Presentation Slides, dan Iconography kustom. +* **Pembuatan Aset Tingkat Lanjut:** Integrasi mendalam dengan image generation berbasis AI untuk membuat aset visual nyata, bukan sekadar placeholder. +* **Arsitektur Enterprise:** Arsitektur Design Token yang lebih komprehensif dan scalable untuk deployment tim skala besar. +* **Priority Support:** Dukungan teknis khusus untuk tim dan profesional yang membutuhkan workflow desain lengkap tanpa gangguan. + +👉 *Untuk detail lebih lanjut tentang upgrade ke tier Premium, kunjungi [uupm.cc](https://uupm.cc).* + +## Instalasi + +### Menggunakan Claude Marketplace (Claude Code) + +Instal langsung di Claude Code dengan dua perintah: + +``` +/plugin marketplace add nextlevelbuilder/ui-ux-pro-max-skill +/plugin install ui-ux-pro-max@ui-ux-pro-max-skill +``` + +### Menggunakan CLI (Direkomendasikan) + +```bash +# Install CLI globally +npm install -g ui-ux-pro-max-cli + +# Go to your project +cd /path/to/your/project + +# Install for your AI assistant +uipro init --ai claude # Claude Code +uipro init --ai cursor # Cursor +uipro init --ai windsurf # Windsurf +uipro init --ai antigravity # Antigravity +uipro init --ai copilot # GitHub Copilot +uipro init --ai kiro # Kiro +uipro init --ai codex # Codex CLI +uipro init --ai qoder # Qoder +uipro init --ai roocode # Roo Code +uipro init --ai gemini # Gemini CLI +uipro init --ai trae # Trae +uipro init --ai opencode # OpenCode +uipro init --ai continue # Continue +uipro init --ai codebuddy # CodeBuddy +uipro init --ai droid # Droid (Factory) +uipro init --ai kilocode # KiloCode +uipro init --ai warp # Warp +uipro init --ai augment # Augment +uipro init --ai codewhale # CodeWhale +uipro init --ai openclaw # OpenClaw +uipro init --ai universal # Universal / Agent Standard (.agents/skills/) +uipro init --ai all # All assistants +``` + +Paket npm-nya adalah `ui-ux-pro-max-cli`; paket tersebut tetap menginstal command `uipro`. Rilis lama `uipro-cli` sudah usang dan tidak boleh digunakan untuk aset saat ini. + +### Instalasi Global (Tersedia untuk Semua Proyek) + +```bash +uipro init --ai claude --global # Install to ~/.claude/skills/ +uipro init --ai cursor --global # Install to ~/.cursor/skills/ +uipro init --ai universal --global # Install to ~/.agents/skills/ +``` + +### Command CLI Lainnya + +```bash +uipro versions # List available versions +uipro update # Refresh skill files from installed CLI package +uipro update --global # Refresh global skill files from installed CLI package +uipro init --offline # Compatibility flag; installs bundled templates +uipro uninstall # Remove skill (auto-detect platform) +uipro uninstall --ai claude # Remove specific platform +uipro uninstall --global # Remove from global install +``` + +## Prasyarat + +Python 3.x diperlukan untuk script pencarian (hanya standard library — script tidak menginstal apa pun dan tidak melakukan network call). + +Periksa apakah Python sudah terinstal: + +```bash +python3 --version +``` + +Jika belum ada, instal sendiri dari [python.org](https://www.python.org/downloads/) atau melalui package manager OS Anda (Homebrew, apt, winget). Langkah instalasi ini ditujukan untuk **Anda sebagai pengguna manusia** — agent AI yang menggunakan skill ini tidak boleh menginstal software di mesin Anda; agent diperintahkan untuk meminta Anda melakukannya. + +## Penggunaan + +### Mode Skill (Aktif Otomatis) + +**Didukung:** Claude Code, Cursor, Windsurf, Antigravity, Codex CLI, Continue, Gemini CLI, OpenCode, Qoder, CodeBuddy, Droid (Factory), KiloCode, Warp, Augment, CodeWhale + +Skill akan aktif otomatis saat Anda meminta pekerjaan UI/UX. Cukup gunakan percakapan secara natural: + +``` +Build a landing page for my SaaS product +``` + +> **Trae**: Beralihlah ke mode **SOLO** terlebih dahulu. Skill akan aktif untuk permintaan UI/UX. + +### Mode Workflow (Slash Command) + +**Didukung:** Kiro, GitHub Copilot, Roo Code, KiloCode + +Gunakan slash command untuk menjalankan skill: + +``` +/ui-ux-pro-max Build a landing page for my SaaS product +``` + +### Contoh Prompt + +``` +Build a landing page for my SaaS product + +Create a dashboard for healthcare analytics + +Design a portfolio website with dark mode + +Make a mobile app UI for e-commerce + +Build a fintech banking app with dark theme +``` + +### Cara Kerjanya + +1. **Anda meminta** - Minta tugas UI/UX apa pun (build, design, create, implement, review, fix, improve) +2. **Design System Dibuat** - AI secara otomatis menghasilkan design system lengkap menggunakan mesin penalaran +3. **Rekomendasi cerdas** - Berdasarkan jenis produk dan kebutuhan Anda, AI menemukan gaya, warna, dan tipografi yang paling sesuai +4. **Pembuatan kode** - Menerapkan UI dengan warna, font, spacing, dan best practice yang tepat +5. **Pemeriksaan sebelum delivery** - Memvalidasi hasil terhadap anti-pattern UI/UX umum + +### Stack yang Didukung + +Skill menyediakan panduan khusus stack untuk: + +| Kategori | Stack | +|----------|-------| +| **Web (HTML)** | HTML + Tailwind (default) | +| **Ekosistem React** | React, Next.js, shadcn/ui | +| **Ekosistem Vue** | Vue, Nuxt.js, Nuxt UI | +| **Angular** | Angular | +| **PHP** | Laravel (Blade, Livewire, Inertia.js) | +| **Web Lainnya** | Svelte, Astro, Three.js | +| **Desktop** | JavaFX, WPF, WinUI 3, Avalonia, Uno Platform, UWP | +| **iOS** | SwiftUI | +| **Android** | Jetpack Compose | +| **Cross-Platform** | React Native, Flutter | + +Cukup sebutkan stack pilihan Anda di prompt, atau biarkan default ke HTML + Tailwind. + +## Command Design System (Lanjutan) + +Untuk mengakses design system generator secara langsung: + +> Catatan: Jika Anda menginstal melalui Continue, ganti `.claude/skills/` dengan `.continue/skills/` pada command di bawah. Untuk Droid (Factory), gunakan `.factory/skills/`. + +```bash +# Generate design system with ASCII output +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness" --design-system -p "Serenity Spa" + +# Generate with Markdown output +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "fintech banking" --design-system -f markdown + +# Domain-specific search +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "glassmorphism" --domain style +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "elegant serif" --domain typography +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "dashboard" --domain chart +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "error summary validation" --domain ux +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "decorative icon aria hidden" --domain icons +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "icon button accessible label" --domain icons +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "orphan heading line balance" --domain ux +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "badge chip label wraps to second line" --domain ux +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "rapid chip animation interrupted" --domain ux + +# Stack-specific guidelines +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "form validation" --stack react +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "responsive layout" --stack html-tailwind +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "chip badge overflow nowrap" --stack html-tailwind +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "tableview binding" --stack javafx +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "atlantafx primer enterprise theme" --stack javafx +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "enterprise tableview density permission" --stack javafx +``` + +Pencarian web-stack memahami versi. Query tanpa major version lama akan mengembalikan panduan yang aktif dan terkini. Istilah legacy atau major version lama yang eksplisit (misalnya, `Svelte 4` atau `Next.js 15`) hanya mengembalikan row legacy yang sudah dikurasi, dengan label `Status` dan `Applies To`; jika tidak ada panduan legacy yang sesuai, pencarian tidak mengembalikan hasil alih-alih mencampur generasi framework. + +### Menyimpan Design System (Pola Master + Overrides) + +Simpan design system ke file untuk **hierarchical retrieval lintas sesi**: + +```bash +# Generate and persist to design-system/MASTER.md +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design-system --persist -p "MyApp" + +# Also create a page-specific override file +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design-system --persist -p "MyApp" --page "dashboard" +``` + +Ini membuat struktur folder `design-system/`: + +``` +design-system/ +├── MASTER.md # Global Source of Truth (colors, typography, spacing, components) +└── pages/ + └── dashboard.md # Page-specific overrides (only deviations from Master) +``` + +**Cara kerja hierarchical retrieval:** +1. Saat membuat halaman tertentu (misalnya, "Checkout"), periksa `design-system/pages/checkout.md` terlebih dahulu +2. Jika file halaman ada, aturannya **menimpa** file Master +3. Jika tidak, gunakan `design-system/MASTER.md` saja + +**Prompt context-aware retrieval:** +``` +I am building the [Page Name] page. Please read design-system/MASTER.md. +Also check if design-system/pages/[page-name].md exists. +If the page file exists, prioritize its rules. +If not, use the Master rules exclusively. +Now, generate the code... +``` + +## Arsitektur & Kontribusi + +### Untuk Pengguna + +Codebase telah direstrukturisasi menggunakan **sistem pembuatan berbasis template**. Semua file khusus platform (`.cursor/`, `.windsurf/`, `.kiro/`, `.factory/`, dll.) sekarang dibuat secara dinamis oleh CLI. + +**Selalu gunakan CLI untuk menginstal:** + +```bash +npm install -g ui-ux-pro-max-cli +uipro init --ai +``` + +Ini memastikan Anda mendapatkan template terbaru yang dibundel bersama package CLI yang terinstal serta struktur file yang tepat untuk agent AI Anda. Update package npm terlebih dahulu ketika rilis baru diterbitkan. + +### Untuk Kontributor + +Jika Anda ingin berkontribusi pada proyek ini: + +```bash +# 1. Clone the repository +git clone https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git +cd ui-ux-pro-max-skill + +# 2. Understand the structure +src/ui-ux-pro-max/ # Source of truth (data, scripts, templates) +cli/ # CLI installer (generates files from templates) +.claude/ # Local dev/test for Claude Code skill +.factory/ # Local dev/test for Droid (Factory) skill + +# 3. Make changes in src/ui-ux-pro-max/ +# - data/*.csv → Database files +# - scripts/*.py → Search engine & design system +# - templates/ → Platform-specific templates + +# 4. Sync to CLI and test locally +cd cli +npm run sync:assets +npm run check:assets +npm run verify:data +npm run typecheck + +# 5. Build and test CLI +# `npm run build` uses Bun when available and falls back to TypeScript compiler output after `npm ci`. +npm run build +node dist/index.js init --ai claude --offline # Test in a temp folder + +# 6. Create PR (never push directly to main) +git checkout -b feat/your-feature +git commit -m "feat: description" +git push -u origin feat/your-feature +gh pr create +``` + +Lihat [CLAUDE.md](CLAUDE.md) untuk panduan development yang lebih rinci. + +### Provenance dan Refresh Katalog + +Ringkasan katalog yang di-commit saat ini mencatat **1.934 Google Fonts yang disetujui** +serta **8 pengecualian review** yang tidak dipromosikan tanpa metadata lisensi resmi yang sesuai. Panduan ikon tetap berisi **105 row terkurasi** (100 import web +Phosphor langsung ditambah panduan React Native/fallback); sedangkan **manifest upstream Phosphor dengan 1.512 ikon** memvalidasi nama, +weight, dan import React/SSR tanpa membanjiri hasil pencarian dengan seluruh +package upstream. + +Development biasa dan CI pull request tidak bergantung pada jaringan. Jalankan gate +offline lengkap, termasuk hash snapshot dan validasi jumlah hasil generate, dengan: + +```bash +npm --prefix cli run verify:data +# Or check only the generated catalog summary: +npm --prefix cli run validate:catalog-summary +``` + +Normalisasi refresh juga dapat diuji sepenuhnya secara offline menggunakan +fixture yang sudah di-commit. Output disimpan ke direktori kandidat sementara dan tidak pernah +menggantikan data canonical: + +```bash +candidate_dir="$(mktemp -d)" +python3 scripts/refresh-google-fonts.py \ + --api-input src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-api.json \ + --metadata-input src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-metadata.json \ + --existing-csv src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-existing.csv \ + --overrides src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-overrides.json \ + --output-csv "$candidate_dir/google-fonts.csv" \ + --license-output "$candidate_dir/google-font-licenses.json" \ + --metadata-revision fixture-catalogs-v1 \ + --verified-at 2026-08-13 --expected-count 2 --approve-changes +python3 scripts/refresh-icon-catalog.py \ + --input src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-core.json \ + --package-json src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-package.json \ + --react-package-json src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-react-package.json \ + --react-exports-input src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-react-exports.json \ + --curated-csv src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/icons-curated.csv \ + --output "$candidate_dir/phosphor-icons-upstream.json" \ + --verified-at 2026-08-13 --expected-count 2 +``` + +Refresh upstream live sengaja diisolasi dalam workflow `refresh-catalogs.yml`, +dijadwalkan setiap Senin pukul 03:17 UTC dan juga tersedia untuk dijalankan secara manual. +Konfigurasikan `GOOGLE_FONTS_API_KEY` sebagai secret GitHub Actions, lalu jalankan dan +unduh artifact review-nya: + +```bash +gh workflow run refresh-catalogs.yml +run_id="$(gh run list --workflow refresh-catalogs.yml --limit 1 --json databaseId --jq '.[0].databaseId')" +gh run watch "$run_id" +gh run download "$run_id" --name "catalog-refresh-review-$run_id" +``` + +Workflow membaca Google Fonts Developer API dan package resmi Phosphor yang di-pin, +menulis kandidat serta unified diff ke artifact, dan hanya memiliki izin read-only +pada repository. Workflow tidak pernah melakukan commit, push, membuka PR, atau merge. Tinjau +laporan perubahan, pengecualian, lisensi, relevance metrics, dan offline gate +sebelum secara manual mempromosikan file kandidat ke `src/ui-ux-pro-max/data/`. + + +## Rilis Otomatis + +Repository ini menggunakan semantic-release dengan Conventional Commits untuk membuat rilis GitHub secara otomatis: + +- Branch `dev` membuat GitHub prerelease beta seperti `2.6.0-beta.1`. +- Branch `main` membuat rilis GitHub stable resmi seperti `2.6.0`. + +Release notes dan `CHANGELOG.md` dibuat dari pesan Conventional Commit. Nomor versi disinkronkan di `skill.json`, `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `cli/package.json`, dan `cli/package-lock.json` selama persiapan rilis. + +Gunakan tipe commit berikut agar version bump benar: + +- `fix:` -> patch release +- `feat:` -> minor release +- `feat!:` atau `BREAKING CHANGE:` -> major release + +Workflow rilis menggunakan `GITHUB_TOKEN` default untuk rilis GitHub dan secret repository `NPM_TOKEN` untuk memublikasikan `ui-ux-pro-max-cli` ke npm. + +## Troubleshooting + +### `uipro: unknown command 'uninstall'` atau `unknown command 'update'` + +Versi `ui-ux-pro-max-cli` yang terinstal sudah usang. Perbarui lalu coba lagi: + +```bash +npm install -g ui-ux-pro-max-cli@latest +uipro uninstall +``` + +### `uipro uninstall` menampilkan "No installed AI skill directories detected" + +Skill terinstal di direktori yang berbeda dari tempat Anda menjalankan command. Pilih salah satu: + +```bash +# Option A — run from the project root where you originally installed it +cd /path/to/your/project +uipro uninstall + +# Option B — remove the global install +uipro uninstall --global + +# Option C — remove manually +rm -rf .claude/skills/ui-ux-pro-max # Claude Code +rm -rf .cursor/skills/ui-ux-pro-max # Cursor +rm -rf .windsurf/skills/ui-ux-pro-max # Windsurf +rm -rf .agents/skills/ui-ux-pro-max # Antigravity / Codex +``` + +### Dialog "Upload a skill" Claude.ai menampilkan "Zip contains too many files (maximum 200)" + +Jangan upload ZIP repository GitHub secara penuh. Itu adalah checkout development yang berisi source code, aset CLI, dokumentasi, preview, dan beberapa skill yang dibundel, sehingga melebihi batas upload 200 file milik Claude. ZIP tersebut bukan artifact upload skill Claude, dan proyek ini saat ini tidak menerbitkan ZIP manual terpisah untuk upload ke Claude.ai. + +Untuk Claude Code, instal melalui Marketplace: + +```bash +/plugin marketplace add nextlevelbuilder/ui-ux-pro-max-skill +/plugin install ui-ux-pro-max@ui-ux-pro-max-skill +``` + +Atau gunakan installer CLI: + +```bash +npx ui-ux-pro-max-cli init --ai claude +``` + +### Instalasi Claude Marketplace gagal dengan "Zip file contains a symbolic link" + +Ini adalah masalah yang diketahui pada versi sebelum v2.5.1. Repository sebelumnya menggunakan symlink secara internal yang tidak dapat ditangani oleh sebagian tool instalasi. **Perbaikan:** gunakan installer CLI sebagai gantinya: + +```bash +npm install -g ui-ux-pro-max-cli +uipro init --ai claude +``` + +Atau tunggu rilis berikutnya yang menyelesaikan masalah ini. + +### `npm install -g ui-ux-pro-max-cli` gagal karena permission error + +Gunakan Node version manager (direkomendasikan), atau lewati instalasi global sepenuhnya: + +```bash +# npx without installing globally +npx ui-ux-pro-max-cli init --ai claude +``` + +### Python tidak ditemukan saat menjalankan command design system + +Script pencarian memerlukan Python 3.x. Instal secara manual dari [python.org](https://www.python.org/downloads/) atau melalui package manager OS Anda (Homebrew, apt, winget). Agent AI tidak boleh menginstalnya untuk Anda — agent diperintahkan untuk meminta Anda melakukannya. + +### Output design system terpotong / field ter-truncate + +Output yang mudah dibaca manusia memotong field panjang pada 300 karakter. Gunakan `--json` untuk mendapatkan data lengkap tanpa pemotongan: + +```bash +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS" --domain style --json +``` + +--- + +## Riwayat Star + +[![Star History Chart](https://star-history.dera.page/svg?repos=nextlevelbuilder/ui-ux-pro-max-skill&type=Date)](https://star-history.dera.page/#nextlevelbuilder/ui-ux-pro-max-skill&Date) + +## Lisensi + +Proyek ini dilisensikan di bawah [MIT License](LICENSE). + +## Agent yang Kompatibel + +Skill ini bekerja dengan: +- [Claude Code](https://claude.com/product/claude-code) +- [AdaL](https://sylph.ai/) - Agent coding yang dapat berkembang secara mandiri ([Dokumentasi](https://docs.sylph.ai/) | [GitHub](https://github.com/SylphAI-Inc/adal-cli)) diff --git a/ui-ux-pro-max/README.ko.md b/ui-ux-pro-max/README.ko.md new file mode 100644 index 00000000..1ce35695 --- /dev/null +++ b/ui-ux-pro-max/README.ko.md @@ -0,0 +1,632 @@ +# [UI UX Pro Max](https://uupm.cc) + +

+ 🇮🇩 Bahasa Indonesia | + 🇰🇷 한국어 | + 🇻🇳 Tiếng Việt | + 🇨🇳 简体中文 | + 🇺🇸 English +

+ +

+ GitHub 릴리스 + 추론 규칙 192개 + 검색 가능한 UI 스타일 79개 + Python 3.x + 라이선스 +

+ +

+ npm + npm 다운로드 수 + GitHub 스타 수 + PayPal +

+ +여러 플랫폼과 프레임워크에서 전문적인 UI/UX를 구축할 수 있도록 디자인 인텔리전스를 제공하는 AI 스킬입니다. + +

+ + UI UX Pro Max + +

+ +

+ 이 프로젝트가 유용하다면 후원을 고려해 주세요:

+ PayPal 후원 +

+ +

+ 다른 프로젝트
+ NextLevelBuilder.io | GoClaw.sh | ClaudeKit.cc | TOSE.sh +

+ +## v2.0의 새로운 기능 + +### 지능형 디자인 시스템 생성 + +v2.0의 핵심 기능은 **디자인 시스템 생성기**입니다. AI 기반 추론 엔진이 프로젝트 요구 사항을 분석하고 몇 초 만에 완전한 맞춤형 디자인 시스템을 생성합니다. + +``` +대상: Serenity Spa - 권장 디자인 시스템 + +패턴: 히어로 중심 + 사회적 증거 + 전환: 신뢰 요소를 활용한 감성 중심 설계 + CTA: 첫 화면에 배치하고 후기 다음에 반복 + 섹션: + 1. 히어로 + 2. 서비스 + 3. 고객 후기 + 4. 예약 + 5. 연락처 + +스타일: Soft UI Evolution + 키워드: 부드러운 그림자, 은은한 깊이, 편안함, 고급스러움, 유기적 형태 + 추천 대상: 웰니스, 뷰티, 라이프스타일 브랜드, 프리미엄 서비스 + 성능: cost:low | 접근성: risk:conditional; 요구 사항 확인 필요 + +색상: + 기본: #E8B4B8 (연한 분홍색) + 보조: #A8D5BA (세이지 그린) + CTA: #D4AF37 (금색) + 배경: #FFF5F5 (따뜻한 흰색) + 텍스트: #2D3436 (차콜) + 참고: 차분한 팔레트에 금색 포인트를 더해 고급스러운 분위기 연출 + +타이포그래피: Cormorant Garamond / Montserrat + 분위기: 우아함, 차분함, 세련됨 + 추천 대상: 명품 브랜드, 웰니스, 뷰티, 에디토리얼 + Google Fonts: https://fonts.google.com/share?selection.family=... + +핵심 효과: + 부드러운 그림자 + 맥락에 맞는 전환 + 은은한 호버 상태 + +피해야 할 요소(안티패턴): + 밝은 네온 색상 + 과한 애니메이션 + 다크 모드 + AI 스타일 보라/분홍 그라디언트 + +전달 전 체크리스트: + [ ] 이모지를 아이콘으로 사용하지 않음(SVG: Heroicons/Lucide 사용) + [ ] 클릭 가능한 모든 요소에 cursor-pointer 적용 + [ ] 플랫폼, 컴포넌트, 사용자 환경설정에 맞는 상호작용 타이밍 적용 + [ ] 라이트 모드: 텍스트 명암비 최소 4.5:1 + [ ] 키보드 탐색 시 포커스 상태 표시 + [ ] prefers-reduced-motion 준수 + [ ] 텍스트, 칩, 배지가 잘리거나 깨지지 않고 재배치 + [ ] 반응형: 375px, 768px, 1024px, 1440px +``` + +### 디자인 시스템 생성 방식 + +``` +1. 사용자 요청 + "내 뷰티 스파의 랜딩 페이지를 만들어 줘" + + ↓ + +2. 다중 도메인 검색(5개 병렬 검색) + • 제품 유형 매칭(192개 분야) + • 스타일 추천(검색 가능 79개, 활성 50개) + • 색상 팔레트 선택(192개 팔레트) + • 랜딩 페이지 패턴(34개 패턴) + • 타이포그래피 조합(74개 글꼴 조합) + + ↓ + +3. 추론 엔진 + • 제품 → UI 분야 규칙 매칭 + • 스타일 우선순위 적용(BM25 순위) + • 산업별 안티패턴 필터링 + • 의사결정 규칙 처리(JSON 조건) + + ↓ + +4. 완전한 디자인 시스템 출력 + 패턴 + 스타일 + 색상 + 타이포그래피 + 효과 + + 피해야 할 안티패턴 + 전달 전 체크리스트 +``` + +### 192개의 산업별 추론 규칙 + +추론 엔진에는 다음 분야에 특화된 규칙이 포함되어 있습니다. + +| 분야 | 예시 | +|------|------| +| **기술 및 SaaS** | SaaS, 마이크로 SaaS, B2B 서비스, 개발자 도구/IDE, AI/챗봇 플랫폼, 사이버 보안 플랫폼 | +| **금융** | 핀테크/암호화폐, 은행, 보험, 개인 재무 추적기, 청구서 및 결제 도구 | +| **헬스케어** | 병원, 약국, 치과, 동물병원, 정신 건강, 복약 알림 | +| **전자상거래** | 일반, 명품, 마켓플레이스(P2P), 구독 상자, 음식 배달 | +| **서비스** | 뷰티/스파, 레스토랑, 호텔, 법률, 홈 서비스, 예약 및 일정 관리 | +| **크리에이티브** | 포트폴리오, 에이전시, 사진, 게임, 음악 스트리밍, 사진/동영상 편집기 | +| **라이프스타일** | 습관 추적기, 요리 및 레시피, 명상, 날씨, 일기, 기분 추적기 | +| **신기술** | Web3/NFT, 공간 컴퓨팅, 양자 컴퓨팅, 자율 드론 함대 | + +각 규칙에는 다음 항목이 포함됩니다. +- **권장 패턴** - 랜딩 페이지 구조 +- **스타일 우선순위** - 가장 적합한 UI 스타일 +- **색상 분위기** - 산업에 적합한 색상 팔레트 +- **타이포그래피 분위기** - 글꼴의 개성 조합 +- **핵심 효과** - 애니메이션 및 상호작용 +- **안티패턴** - 피해야 할 요소(예: 은행 서비스의 "AI 스타일 보라색/분홍색 그라디언트") + +## 기능 + +- **검색 가능한 UI 스타일 79개(활성 50개)** - 글래스모피즘, 클레이모피즘, 미니멀리즘, 브루탈리즘, 뉴모피즘, 벤토 그리드, 다크 모드, AI 네이티브 UI 등 +- **색상 팔레트 192개** - 192개 제품 유형과 1:1로 정렬된 산업별 팔레트 +- **글꼴 조합 74개** - Google Fonts 가져오기 코드가 포함된 엄선된 타이포그래피 조합 +- **차트 유형 25개** - 대시보드 및 분석 화면을 위한 권장 사항 +- **기술 스택 22개** - React, Next.js, Astro, Vue, Nuxt.js, Nuxt UI, Svelte, SwiftUI, React Native, Flutter, HTML+Tailwind, shadcn/ui, Jetpack Compose, Angular, Laravel, Three.js, JavaFX, WPF, WinUI 3, UWP, Avalonia, Uno Platform +- **UX 가이드라인 119개** - 모범 사례, 안티패턴, 접근성 규칙, 유연한 텍스트 레이아웃, 간결한 레이블, 취소 가능한 상호작용 +- **추론 규칙 192개** - 산업별 디자인 시스템 생성(v2.0의 새로운 기능) + +### 유연한 텍스트와 컴팩트 UI + +이 가이드는 제목, 긴 토큰, 칩, 배지, 중단된 마이크로 인터랙션에서 흔히 발생하는 실제 서비스 문제를 다룹니다. + +- 제목의 균형 잡힌 줄바꿈은 점진적 향상 기능일 뿐, 특정 단어가 마지막 줄에 남는다는 보장은 아닙니다. 너비, 글꼴, 로케일에 따른 자연스러운 줄바꿈에서도 디자인이 정상적으로 작동해야 합니다. +- 필수 텍스트는 좁은 화면, 브라우저 확대, 텍스트 크기 조정, 사용자 간격 설정에서도 잘리지 않고 재배치되어야 합니다. 긴 URL과 식별자는 안전하게 줄바꿈할 수 있어야 합니다. +- 칩과 태그 모음은 줄바꿈되거나 조작 가능한 `+n` 펼치기 기능을 사용해야 합니다. 간결한 레이블은 가능한 한 온전하게 유지하고, 불가피하게 잘라야 한다면 키보드·포인터·터치 사용자가 전체 값을 확인할 수 있는 접근 가능한 경로를 제공해야 합니다. +- 배지의 의미를 색상에만 의존해서는 안 됩니다. 상호작용 가능한 칩에는 네이티브 시맨틱, 명확한 포커스, 프로그래밍 방식의 상태가 필요하며 실시간 개수에는 의미 있는 맥락이 필요합니다. +- 빠른 상호작용은 애니메이션을 취소할 수 있지만, 최종 시맨틱 상태·포커스·콘텐츠는 정확해야 합니다. 타이밍은 플랫폼과 컴포넌트에 맞게 선택하고 모션 감소 환경설정을 존중해야 합니다. + +### 스타일 분류 체계 + +카탈로그에는 안정적인 ID와 별칭으로 뒷받침되는 **검색 가능한 스타일 79개**가 포함됩니다. + +| 상태 | 개수 | 검색 동작 | +|------|-----:|-----------| +| 활성 | 50 | 일반 추천에 포함되며 갤러리에 기본 표시 | +| 보조 | 29 | 정확하거나 명시적인 변형/시스템 의도일 때 반환되며 갤러리 상태 필터에서 사용 가능 | +| 사용 중단 | 9 | 일반 순위에서 제외되며 기존 이름은 표준 스타일 또는 랜딩 패턴으로 연결 | + +활성 세트는 일반 시각 스타일군 43개, 모바일 전용 스타일 2개, 공식 플랫폼/디자인 시스템 3개, 플랫폼 소재 1개, 핵심 분석 스타일 1개를 포함합니다. 현재 공식 시스템에는 Fluent 2, Shopify Polaris, Adobe Spectrum이 포함됩니다. Liquid Glass는 Apple 플랫폼 소재로 한정되고, Material 3 Expressive는 모바일 Material 변형으로 유지되며, Spectrum 2는 보조 스타일입니다. 랜딩 페이지 구조는 BM25 순위에서 시각 스타일과 경쟁하지 않고 별도의 34개 패턴 랜딩 데이터셋에 포함됩니다. + +전체 분류 체계와 출처 인식 메타데이터는 [`styles.csv`](src/ui-ux-pro-max/data/styles.csv)를 참고하세요. + +## 💎 기본 버전과 프리미엄 버전 비교 + +많은 사용자가 오픈 소스 버전과 프리미엄 버전의 차이를 묻습니다. 워크플로에 적합한 버전을 선택할 수 있도록 자세히 비교했습니다. + +### 🟢 기본 버전(이 저장소) +* **완전한 오픈 소스:** 개인 개발자, 취미 개발자, 일반 프로젝트에 적합합니다. +* **핵심 UI/UX 인텔리전스:** 검색 가능한 UI 스타일 79개(활성 50개), 제품 유형 192개, 색상 팔레트, 엄선된 글꼴 조합을 모두 사용할 수 있습니다. +* **스마트 추천:** 내장 BM25 검색 엔진으로 정확도 높은 디자인 매칭을 제공합니다. +* **크로스 플랫폼 지원:** 22개 주요 프레임워크(React, Vue, Tailwind, iOS, Android 등)를 위한 스택별 가이드라인을 제공합니다. +* **디자인 시스템 생성:** CLI를 통해 맞춤형 UI 규칙, 패턴, 로직을 즉시 생성합니다. + +### 🟡 프리미엄 버전 +* **확장된 브랜드 디자인 스킬:** UI/UX를 넘어 브랜드 아이덴티티 생성, 로고 디자인, 기업 아이덴티티 프로그램(CIP), 배너, 프레젠테이션 슬라이드, 맞춤형 아이콘 제작을 포함합니다. +* **고급 에셋 생성:** AI 기반 이미지 생성과 긴밀하게 통합되어 플레이스홀더가 아닌 실제 시각 에셋을 만듭니다. +* **엔터프라이즈 아키텍처:** 대규모 팀 배포를 위한 더 포괄적이고 확장 가능한 디자인 토큰 아키텍처를 제공합니다. +* **우선 지원:** 중단 없는 전체 디자인 워크플로가 필요한 팀과 전문가에게 전용 기술 지원을 제공합니다. + +👉 *프리미엄 등급 업그레이드에 관한 자세한 내용은 [uupm.cc](https://uupm.cc)를 참고하세요.* + +## 설치 + +### Claude Marketplace 사용(Claude Code) + +Claude Code에서 다음 두 명령어로 바로 설치할 수 있습니다. + +``` +/plugin marketplace add nextlevelbuilder/ui-ux-pro-max-skill +/plugin install ui-ux-pro-max@ui-ux-pro-max-skill +``` + +### CLI 사용(권장) + +```bash +# CLI 전역 설치 +npm install -g ui-ux-pro-max-cli + +# 프로젝트로 이동 +cd /path/to/your/project + +# 사용하는 AI 어시스턴트용으로 설치 +uipro init --ai claude # Claude Code +uipro init --ai cursor # Cursor +uipro init --ai windsurf # Windsurf +uipro init --ai antigravity # Antigravity +uipro init --ai copilot # GitHub Copilot +uipro init --ai kiro # Kiro +uipro init --ai codex # Codex CLI +uipro init --ai qoder # Qoder +uipro init --ai roocode # Roo Code +uipro init --ai gemini # Gemini CLI +uipro init --ai trae # Trae +uipro init --ai opencode # OpenCode +uipro init --ai continue # Continue +uipro init --ai codebuddy # CodeBuddy +uipro init --ai droid # Droid (Factory) +uipro init --ai kilocode # KiloCode +uipro init --ai warp # Warp +uipro init --ai augment # Augment +uipro init --ai codewhale # CodeWhale +uipro init --ai openclaw # OpenClaw +uipro init --ai universal # Universal / Agent Standard (.agents/skills/) +uipro init --ai all # All assistants +``` + +npm 패키지명은 `ui-ux-pro-max-cli`이며, 설치되는 명령어는 `uipro`입니다. 이전 `uipro-cli` 릴리스는 오래되었으므로 현재 에셋과 함께 사용하지 마세요. + +### 전역 설치(모든 프로젝트에서 사용) + +```bash +uipro init --ai claude --global # Install to ~/.claude/skills/ +uipro init --ai cursor --global # Install to ~/.cursor/skills/ +uipro init --ai universal --global # Install to ~/.agents/skills/ +``` + +### 기타 CLI 명령어 + +```bash +uipro versions # List available versions +uipro update # Refresh skill files from installed CLI package +uipro update --global # Refresh global skill files from installed CLI package +uipro init --offline # Compatibility flag; installs bundled templates +uipro uninstall # Remove skill (auto-detect platform) +uipro uninstall --ai claude # Remove specific platform +uipro uninstall --global # Remove from global install +``` + +## 사전 요구 사항 + +검색 스크립트를 실행하려면 Python 3.x가 필요합니다. 표준 라이브러리만 사용하며, 스크립트는 아무것도 설치하지 않고 네트워크 요청도 보내지 않습니다. + +Python 설치 여부를 확인하세요. + +```bash +python3 --version +``` + +설치되어 있지 않다면 [python.org](https://www.python.org/downloads/) 또는 운영체제의 패키지 관리자(Homebrew, apt, winget)를 사용해 직접 설치하세요. 이 설치 단계는 **사람인 사용자**가 수행해야 합니다. 이 스킬을 사용하는 AI 에이전트는 사용자 컴퓨터에 소프트웨어를 직접 설치하지 않고, 사용자에게 설치를 요청하도록 지시되어 있습니다. + +## 사용법 + +### 스킬 모드(자동 활성화) + +**지원:** Claude Code, Cursor, Windsurf, Antigravity, Codex CLI, Continue, Gemini CLI, OpenCode, Qoder, CodeBuddy, Droid (Factory), KiloCode, Warp, Augment, CodeWhale + +UI/UX 작업을 요청하면 스킬이 자동으로 활성화됩니다. 자연스럽게 요청하세요. + +``` +내 SaaS 제품의 랜딩 페이지를 만들어 줘 +``` + +> **Trae**: 먼저 **SOLO** 모드로 전환하세요. UI/UX 요청 시 스킬이 활성화됩니다. + +### 워크플로 모드(슬래시 명령어) + +**지원:** Kiro, GitHub Copilot, Roo Code, KiloCode + +슬래시 명령어로 스킬을 실행하세요. + +``` +/ui-ux-pro-max 내 SaaS 제품의 랜딩 페이지를 만들어 줘 +``` + +### 프롬프트 예시 + +``` +내 SaaS 제품의 랜딩 페이지를 만들어 줘 + +헬스케어 분석 대시보드를 만들어 줘 + +다크 모드 포트폴리오 웹사이트를 디자인해 줘 + +전자상거래 모바일 앱 UI를 만들어 줘 + +어두운 테마의 핀테크 뱅킹 앱을 만들어 줘 +``` + +### 작동 방식 + +1. **요청** - UI/UX 작업을 요청합니다(구축, 디자인, 생성, 구현, 검토, 수정, 개선). +2. **디자인 시스템 생성** - AI가 추론 엔진을 사용해 완전한 디자인 시스템을 자동으로 생성합니다. +3. **스마트 추천** - 제품 유형과 요구 사항에 따라 가장 적합한 스타일, 색상, 타이포그래피를 찾습니다. +4. **코드 생성** - 적절한 색상, 글꼴, 간격, 모범 사례를 적용해 UI를 구현합니다. +5. **전달 전 검사** - 일반적인 UI/UX 안티패턴을 기준으로 검증합니다. + +### 지원 스택 + +이 스킬은 다음 스택별 가이드라인을 제공합니다. + +| 분야 | 스택 | +|------|------| +| **웹(HTML)** | HTML + Tailwind(기본값) | +| **React 생태계** | React, Next.js, shadcn/ui | +| **Vue 생태계** | Vue, Nuxt.js, Nuxt UI | +| **Angular** | Angular | +| **PHP** | Laravel(Blade, Livewire, Inertia.js) | +| **기타 웹** | Svelte, Astro, Three.js | +| **데스크톱** | JavaFX, WPF, WinUI 3, Avalonia, Uno Platform, UWP | +| **iOS** | SwiftUI | +| **Android** | Jetpack Compose | +| **크로스 플랫폼** | React Native, Flutter | + +프롬프트에서 원하는 스택을 언급하거나 기본값인 HTML + Tailwind를 사용하세요. + +## 디자인 시스템 명령어(고급) + +디자인 시스템 생성기에 직접 접근하려면 다음 명령어를 사용하세요. + +> 참고: Continue로 설치했다면 아래 명령어의 `.claude/skills/`를 `.continue/skills/`로 바꾸세요. Droid(Factory)는 `.factory/skills/`를 사용합니다. + +```bash +# ASCII 출력으로 디자인 시스템 생성 +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness" --design-system -p "Serenity Spa" + +# Markdown 출력으로 생성 +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "fintech banking" --design-system -f markdown + +# 도메인별 검색 +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "glassmorphism" --domain style +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "elegant serif" --domain typography +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "dashboard" --domain chart +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "error summary validation" --domain ux +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "decorative icon aria hidden" --domain icons +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "icon button accessible label" --domain icons +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "orphan heading line balance" --domain ux +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "badge chip label wraps to second line" --domain ux +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "rapid chip animation interrupted" --domain ux + +# 스택별 가이드라인 +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "form validation" --stack react +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "responsive layout" --stack html-tailwind +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "chip badge overflow nowrap" --stack html-tailwind +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "tableview binding" --stack javafx +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "atlantafx primer enterprise theme" --stack javafx +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "enterprise tableview density permission" --stack javafx +``` + +웹 스택 검색은 버전을 인식합니다. 이전 메이저 버전을 지정하지 않은 쿼리는 현재 활성 가이드라인을 반환합니다. 명시적인 레거시 용어나 이전 메이저 버전(예: `Svelte 4`, `Next.js 15`)을 지정하면 `Status`와 `Applies To`가 표시된 엄선된 레거시 행만 반환합니다. 일치하는 레거시 가이드라인이 없다면 프레임워크 세대를 섞지 않고 결과를 반환하지 않습니다. + +### 디자인 시스템 저장(마스터 + 오버라이드 패턴) + +세션 간 **계층적 검색**을 위해 디자인 시스템을 파일로 저장하세요. + +```bash +# 디자인 시스템을 생성하여 design-system/myapp/MASTER.md에 저장 +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design-system --persist -p "MyApp" + +# 페이지별 오버라이드 파일도 생성 +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design-system --persist -p "MyApp" --page "dashboard" +``` + +다음과 같은 `design-system/` 폴더 구조가 생성됩니다. + +``` +design-system/ +└── myapp/ # 프로젝트마다 폴더 하나(-p "MyApp"의 슬러그) + ├── MASTER.md # 전역 단일 정보 출처(색상, 타이포그래피, 간격, 컴포넌트) + └── pages/ + └── dashboard.md # 페이지별 오버라이드(마스터와 다른 내용만 기록) +``` + +**계층적 검색 방식:** +1. 특정 페이지(예: "결제")를 만들 때 먼저 `design-system/[project-slug]/pages/checkout.md`를 확인합니다. +2. 페이지 파일이 있으면 해당 규칙이 마스터 파일을 **재정의**합니다. +3. 없으면 `design-system/[project-slug]/MASTER.md`만 사용합니다. + +**컨텍스트 인식 검색 프롬프트:** +``` +[페이지 이름] 페이지를 만들고 있습니다. design-system/[project-slug]/MASTER.md를 읽어 주세요. +design-system/[project-slug]/pages/[page-name].md 파일이 있는지도 확인해 주세요. +페이지 파일이 있으면 해당 규칙을 우선 적용하세요. +없으면 마스터 규칙만 사용하세요. +이제 코드를 생성해 주세요... +``` + +## 아키텍처 및 기여 + +### 사용자 안내 + +코드베이스는 **템플릿 기반 생성 시스템**을 사용하도록 재구성되었습니다. 모든 플랫폼별 파일(`.cursor/`, `.windsurf/`, `.kiro/`, `.factory/` 등)은 이제 CLI에서 동적으로 생성됩니다. + +**항상 CLI를 사용해 설치하세요.** + +```bash +npm install -g ui-ux-pro-max-cli +uipro init --ai +``` + +이 방법을 사용하면 설치된 CLI 패키지에 포함된 최신 템플릿과 AI 어시스턴트에 맞는 올바른 파일 구조를 받을 수 있습니다. 새 릴리스가 배포되면 먼저 npm 패키지를 업데이트하세요. + +### 기여자 안내 + +이 프로젝트에 기여하려면 다음 절차를 따르세요. + +```bash +# 1. 저장소 복제 +git clone https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git +cd ui-ux-pro-max-skill + +# 2. 구조 이해 +src/ui-ux-pro-max/ # 단일 정보 출처(데이터, 스크립트, 템플릿) +cli/ # CLI 설치 도구(템플릿에서 파일 생성) +.claude/ # Claude Code 스킬 로컬 개발/테스트 +.factory/ # Droid(Factory) 스킬 로컬 개발/테스트 + +# 3. src/ui-ux-pro-max/에서 변경 +# - data/*.csv → 데이터베이스 파일 +# - scripts/*.py → 검색 엔진 및 디자인 시스템 +# - templates/ → 플랫폼별 템플릿 + +# 4. CLI에 동기화하고 로컬 테스트 +cd cli +npm run sync:assets +npm run check:assets +npm run verify:data +npm run typecheck + +# 5. CLI 빌드 및 테스트 +# `npm run build`는 Bun이 있으면 사용하고, 없으면 `npm ci` 후 TypeScript 컴파일러 출력을 사용합니다. +npm run build +node dist/index.js init --ai claude --offline # Test in a temp folder + +# 6. PR 생성(main에 직접 푸시하지 않음) +git checkout -b feat/your-feature +git commit -m "feat: description" +git push -u origin feat/your-feature +gh pr create +``` + +자세한 개발 가이드라인은 [CLAUDE.md](CLAUDE.md)를 참고하세요. + +### 카탈로그 출처 및 갱신 + +커밋된 카탈로그 요약에는 현재 **승인된 Google Fonts 1,934개**와 공식 라이선스 메타데이터가 일치하지 않아 반영되지 않은 **검토 제외 항목 8개**가 기록되어 있습니다. 아이콘 가이드는 **엄선된 105개 행**(Phosphor 웹 직접 가져오기 100개와 React Native/대체 가이드)으로 유지됩니다. 별도의 **1,512개 아이콘이 포함된 업스트림 Phosphor 매니페스트**는 검색 결과를 업스트림 패키지 전체로 채우지 않으면서 이름, 두께, React/SSR 가져오기를 검증합니다. + +일반 개발 및 풀 리퀘스트 CI는 네트워크에 의존하지 않습니다. 스냅샷 해시와 생성 개수 검증을 포함한 전체 오프라인 게이트를 실행하려면 다음 명령어를 사용하세요. + +```bash +npm --prefix cli run verify:data +# 또는 생성된 카탈로그 요약만 검사: +npm --prefix cli run validate:catalog-summary +``` + +갱신 정규화도 커밋된 픽스처를 사용해 완전히 오프라인으로 실행할 수 있습니다. 출력은 임시 후보 디렉터리에 저장되며 표준 데이터를 대체하지 않습니다. + +```bash +candidate_dir="$(mktemp -d)" +python3 scripts/refresh-google-fonts.py \ + --api-input src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-api.json \ + --metadata-input src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-metadata.json \ + --existing-csv src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-existing.csv \ + --overrides src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/google-overrides.json \ + --output-csv "$candidate_dir/google-fonts.csv" \ + --license-output "$candidate_dir/google-font-licenses.json" \ + --metadata-revision fixture-catalogs-v1 \ + --verified-at 2026-08-13 --expected-count 2 --approve-changes +python3 scripts/refresh-icon-catalog.py \ + --input src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-core.json \ + --package-json src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-package.json \ + --react-package-json src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-react-package.json \ + --react-exports-input src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/phosphor-react-exports.json \ + --curated-csv src/ui-ux-pro-max/scripts/tests/fixtures/catalogs/icons-curated.csv \ + --output "$candidate_dir/phosphor-icons-upstream.json" \ + --verified-at 2026-08-13 --expected-count 2 +``` + +실시간 업스트림 갱신은 의도적으로 `refresh-catalogs.yml` 워크플로에 격리되어 있으며, 매주 월요일 03:17 UTC에 실행되거나 필요할 때 수동으로 실행할 수 있습니다. `GOOGLE_FONTS_API_KEY`를 GitHub Actions 시크릿으로 설정한 다음 워크플로를 실행하고 검토용 아티팩트를 다운로드하세요. + +```bash +gh workflow run refresh-catalogs.yml +run_id="$(gh run list --workflow refresh-catalogs.yml --limit 1 --json databaseId --jq '.[0].databaseId')" +gh run watch "$run_id" +gh run download "$run_id" --name "catalog-refresh-review-$run_id" +``` + +이 워크플로는 Google Fonts Developer API와 버전이 고정된 공식 Phosphor 패키지를 읽고, 후보 파일과 통합 diff를 아티팩트에 기록하며, 저장소 읽기 전용 권한만 가집니다. 커밋, 푸시, PR 생성, 병합은 수행하지 않습니다. 후보 파일을 `src/ui-ux-pro-max/data/`에 수동으로 반영하기 전에 변경 보고서, 제외 항목, 라이선스, 관련성 지표, 오프라인 게이트를 검토하세요. + + +## 자동 릴리스 + +이 저장소는 Conventional Commits와 semantic-release를 사용해 GitHub 릴리스를 자동으로 생성합니다. + +- `dev` 브랜치는 `2.6.0-beta.1`과 같은 베타 GitHub 프리릴리스를 생성합니다. +- `main` 브랜치는 `2.6.0`과 같은 공식 안정 릴리스를 생성합니다. + +릴리스 노트와 `CHANGELOG.md`는 Conventional Commit 메시지에서 생성됩니다. 릴리스 준비 중 `skill.json`, `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `cli/package.json`, `cli/package-lock.json`의 버전 번호가 동기화됩니다. + +올바른 버전 증가를 위해 다음 커밋 유형을 사용하세요. + +- `fix:` -> 패치 릴리스 +- `feat:` -> 마이너 릴리스 +- `feat!:` 또는 `BREAKING CHANGE:` -> 메이저 릴리스 + +릴리스 워크플로는 GitHub 릴리스에 기본 `GITHUB_TOKEN`을 사용하고, `ui-ux-pro-max-cli`를 npm에 배포할 때 저장소의 `NPM_TOKEN` 시크릿을 사용합니다. + +## 문제 해결 + +### `uipro: unknown command 'uninstall'` 또는 `unknown command 'update'` + +설치된 `ui-ux-pro-max-cli` 버전이 오래되었습니다. 업데이트한 후 다시 시도하세요. + +```bash +npm install -g ui-ux-pro-max-cli@latest +uipro uninstall +``` + +### `uipro uninstall` 실행 시 "No installed AI skill directories detected"가 표시되는 경우 + +명령어를 실행한 디렉터리가 스킬을 설치한 디렉터리와 다릅니다. 다음 중 하나를 수행하세요. + +```bash +# 방법 A — 처음 설치한 프로젝트 루트에서 실행 +cd /path/to/your/project +uipro uninstall + +# 방법 B — 전역 설치 제거 +uipro uninstall --global + +# 방법 C — 수동 제거 +rm -rf .claude/skills/ui-ux-pro-max # Claude Code +rm -rf .cursor/skills/ui-ux-pro-max # Cursor +rm -rf .windsurf/skills/ui-ux-pro-max # Windsurf +rm -rf .agents/skills/ui-ux-pro-max # Antigravity / Codex +``` + +### Claude.ai의 "Upload a skill" 대화상자에서 "Zip contains too many files (maximum 200)"가 표시되는 경우 + +GitHub 저장소 전체 ZIP을 업로드하지 마세요. 이 ZIP은 소스 코드, CLI 에셋, 문서, 미리보기, 여러 번들 스킬을 포함한 개발용 체크아웃이므로 Claude의 파일 200개 업로드 제한을 초과합니다. Claude 스킬 업로드용 아티팩트가 아니며, 이 프로젝트는 현재 Claude.ai 수동 업로드용 ZIP을 별도로 배포하지 않습니다. + +Claude Code에서는 Marketplace를 통해 설치하세요. + +```bash +/plugin marketplace add nextlevelbuilder/ui-ux-pro-max-skill +/plugin install ui-ux-pro-max@ui-ux-pro-max-skill +``` + +또는 CLI 설치 도구를 사용하세요. + +```bash +npx ui-ux-pro-max-cli init --ai claude +``` + +### Claude Marketplace 설치가 "Zip file contains a symbolic link" 오류로 실패하는 경우 + +v2.5.1 이전 버전에서 알려진 문제입니다. 저장소 내부에서 일부 설치 도구가 처리할 수 없는 심볼릭 링크를 사용했습니다. **해결 방법:** CLI 설치 도구를 사용하세요. + +```bash +npm install -g ui-ux-pro-max-cli +uipro init --ai claude +``` + +또는 이 문제가 해결된 다음 릴리스를 기다리세요. + +### `npm install -g ui-ux-pro-max-cli` 명령이 권한 오류로 실패하는 경우 + +Node 버전 관리자를 사용하거나(권장) 전역 설치를 생략하세요. + +```bash +# 전역 설치 없이 npx 사용 +npx ui-ux-pro-max-cli init --ai claude +``` + +### 디자인 시스템 명령어 실행 시 Python을 찾을 수 없는 경우 + +검색 스크립트에는 Python 3.x가 필요합니다. [python.org](https://www.python.org/downloads/) 또는 운영체제의 패키지 관리자(Homebrew, apt, winget)를 사용해 직접 설치하세요. AI 에이전트는 대신 설치하지 않으며 사용자에게 설치를 요청하도록 지시되어 있습니다. + +### 디자인 시스템 출력 또는 필드가 잘리는 경우 + +사람이 읽기 쉬운 출력에서는 긴 필드가 300자로 잘립니다. 잘리지 않은 전체 데이터를 받으려면 `--json`을 사용하세요. + +```bash +python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS" --domain style --json +``` + +--- + +## 스타 기록 + +[![스타 기록 차트](https://star-history.dera.page/svg?repos=nextlevelbuilder/ui-ux-pro-max-skill&type=Date)](https://star-history.dera.page/#nextlevelbuilder/ui-ux-pro-max-skill&Date) + +## 라이선스 + +이 프로젝트는 [MIT 라이선스](LICENSE)에 따라 배포됩니다. + +## 호환 에이전트 + +이 스킬은 다음 에이전트에서 사용할 수 있습니다. +- [Claude Code](https://claude.com/product/claude-code) +- [AdaL](https://sylph.ai/) - 스스로 진화하는 AI 코딩 에이전트([문서](https://docs.sylph.ai/) | [GitHub](https://github.com/SylphAI-Inc/adal-cli)) diff --git a/ui-ux-pro-max/README.md b/ui-ux-pro-max/README.md index 6b716338..e2c41d2b 100755 --- a/ui-ux-pro-max/README.md +++ b/ui-ux-pro-max/README.md @@ -1,6 +1,8 @@ # [UI UX Pro Max](https://uupm.cc)

+ 🇮🇩 Bahasa Indonesia | + 🇰🇷 한국어 | 🇻🇳 Tiếng Việt | 🇨🇳 简体中文 | 🇺🇸 English @@ -36,7 +38,7 @@ An AI skill that provides design intelligence for building professional UI/UX ac

Other projects
- NextLevelBuilder.io | GoClaw.sh | ClaudeKit.cc | TOSE.sh + NextLevelBuilder.io | GoClaw.sh | AgentKit.best | TOSE.sh

## What's New in v2.0 @@ -405,7 +407,7 @@ results instead of mixing framework generations. Save your design system to files for **hierarchical retrieval across sessions**: ```bash -# Generate and persist to design-system/MASTER.md +# Generate and persist to design-system/myapp/MASTER.md python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design-system --persist -p "MyApp" # Also create a page-specific override file @@ -416,20 +418,21 @@ This creates a `design-system/` folder structure: ``` design-system/ -├── MASTER.md # Global Source of Truth (colors, typography, spacing, components) -└── pages/ - └── dashboard.md # Page-specific overrides (only deviations from Master) +└── myapp/ # One folder per project (slug of -p "MyApp") + ├── MASTER.md # Global Source of Truth (colors, typography, spacing, components) + └── pages/ + └── dashboard.md # Page-specific overrides (only deviations from Master) ``` **How hierarchical retrieval works:** -1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md` +1. When building a specific page (e.g., "Checkout"), first check `design-system/[project-slug]/pages/checkout.md` 2. If the page file exists, its rules **override** the Master file -3. If not, use `design-system/MASTER.md` exclusively +3. If not, use `design-system/[project-slug]/MASTER.md` exclusively **Context-aware retrieval prompt:** ``` -I am building the [Page Name] page. Please read design-system/MASTER.md. -Also check if design-system/pages/[page-name].md exists. +I am building the [Page Name] page. Please read design-system/[project-slug]/MASTER.md. +Also check if design-system/[project-slug]/pages/[page-name].md exists. If the page file exists, prioritize its rules. If not, use the Master rules exclusively. Now, generate the code... diff --git a/ui-ux-pro-max/README.vi.md b/ui-ux-pro-max/README.vi.md index 2219c72f..d5e4551a 100644 --- a/ui-ux-pro-max/README.vi.md +++ b/ui-ux-pro-max/README.vi.md @@ -1,6 +1,8 @@ # [UI UX Pro Max](https://uupm.cc)

+ 🇮🇩 Bahasa Indonesia | + 🇰🇷 한국어 | 🇻🇳 Tiếng Việt | 🇨🇳 简体中文 | 🇺🇸 English @@ -394,7 +396,7 @@ Tìm kiếm cho web stack có nhận biết phiên bản. Truy vấn không nêu Lưu hệ thống thiết kế vào tệp để **truy xuất phân cấp giữa các phiên làm việc**: ```bash -# Tạo và lưu vào design-system/MASTER.md +# Tạo và lưu vào design-system/myapp/MASTER.md python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design-system --persist -p "MyApp" # Đồng thời tạo tệp ghi đè riêng cho một trang @@ -405,22 +407,23 @@ Lệnh trên tạo cấu trúc thư mục `design-system/`: ``` design-system/ -├── MASTER.md # Nguồn tham chiếu chung (màu sắc, kiểu chữ, khoảng cách, thành phần) -└── pages/ - └── dashboard.md # Ghi đè riêng cho trang (chỉ những điểm khác với Master) +└── myapp/ # Mỗi dự án một thư mục (slug của -p "MyApp") + ├── MASTER.md # Nguồn tham chiếu chung (màu sắc, kiểu chữ, khoảng cách, thành phần) + └── pages/ + └── dashboard.md # Ghi đè riêng cho trang (chỉ những điểm khác với Master) ``` **Cách truy xuất phân cấp hoạt động:** -1. Khi xây dựng một trang cụ thể (ví dụ: "Checkout"), trước tiên kiểm tra `design-system/pages/checkout.md` +1. Khi xây dựng một trang cụ thể (ví dụ: "Checkout"), trước tiên kiểm tra `design-system/[project-slug]/pages/checkout.md` 2. Nếu tệp của trang tồn tại, các quy tắc trong đó sẽ **ghi đè** tệp Master -3. Nếu không, chỉ sử dụng `design-system/MASTER.md` +3. Nếu không, chỉ sử dụng `design-system/[project-slug]/MASTER.md` **Prompt truy xuất theo ngữ cảnh:** ``` -Tôi đang xây dựng trang [Tên trang]. Hãy đọc design-system/MASTER.md. -Đồng thời kiểm tra xem design-system/pages/[page-name].md có tồn tại hay không. +Tôi đang xây dựng trang [Tên trang]. Hãy đọc design-system/[project-slug]/MASTER.md. +Đồng thời kiểm tra xem design-system/[project-slug]/pages/[page-name].md có tồn tại hay không. Nếu tệp của trang tồn tại, hãy ưu tiên các quy tắc trong đó. Nếu không, chỉ sử dụng các quy tắc trong tệp Master. Bây giờ, hãy tạo mã nguồn... diff --git a/ui-ux-pro-max/README.zh.md b/ui-ux-pro-max/README.zh.md index be7c4551..d636a678 100644 --- a/ui-ux-pro-max/README.zh.md +++ b/ui-ux-pro-max/README.zh.md @@ -1,6 +1,8 @@ # [UI UX Pro Max](https://uupm.cc)

+ 🇮🇩 Bahasa Indonesia | + 🇰🇷 한국어 | 🇻🇳 Tiếng Việt | 🇨🇳 简体中文 | 🇺🇸 English @@ -399,7 +401,7 @@ legacy 条目,并通过 `Status` 和 `Applies To` 标识。若没有对应的 将设计系统保存到文件,实现**跨会话的层级检索**: ```bash -# 生成并持久化到 design-system/MASTER.md +# 生成并持久化到 design-system/myapp/MASTER.md python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design-system --persist -p "MyApp" # 同时创建页面特定的覆盖文件 @@ -410,20 +412,21 @@ python3 .claude/skills/ui-ux-pro-max/scripts/search.py "SaaS dashboard" --design ``` design-system/ -├── MASTER.md # 全局唯一真相源 (颜色、字体、间距、组件) -└── pages/ - └── dashboard.md # 页面特定覆盖 (仅与主配置的偏差) +└── myapp/ # 每个项目一个文件夹 (-p "MyApp" 的 slug) + ├── MASTER.md # 全局唯一真相源 (颜色、字体、间距、组件) + └── pages/ + └── dashboard.md # 页面特定覆盖 (仅与主配置的偏差) ``` **层级检索工作原理:** -1. 构建特定页面 (如"结账页") 时,先检查 `design-system/pages/checkout.md` +1. 构建特定页面 (如"结账页") 时,先检查 `design-system/[project-slug]/pages/checkout.md` 2. 如果页面文件存在,其规则**覆盖**主配置文件 -3. 如果不存在,仅使用 `design-system/MASTER.md` +3. 如果不存在,仅使用 `design-system/[project-slug]/MASTER.md` **上下文感知检索提示词:** ``` -我正在构建 [页面名称] 页面。请阅读 design-system/MASTER.md。 -同时检查 design-system/pages/[page-name].md 是否存在。 +我正在构建 [页面名称] 页面。请阅读 design-system/[project-slug]/MASTER.md。 +同时检查 design-system/[project-slug]/pages/[page-name].md 是否存在。 如果页面文件存在,优先使用其规则。 如果不存在,仅使用主配置规则。 现在,生成代码... diff --git a/ui-ux-pro-max/SOURCE.md b/ui-ux-pro-max/SOURCE.md index 0e7f27d3..37d67a3e 100644 --- a/ui-ux-pro-max/SOURCE.md +++ b/ui-ux-pro-max/SOURCE.md @@ -1,8 +1,8 @@ # Source - Repo: https://github.com/nextlevelbuilder/ui-ux-pro-max-skill -- Ref: 8bd29e775453ebcae52b6e6514fbf134df0c5770 +- Ref: 91c193ac059b487d13ce535c7015b021eb71c841 - Remove-Paths: -- Snapshot: 2026-08-27 +- Snapshot: 2026-09-03 - Sync-Mode: render_skill - Notes: vendored into playbook branch thirdparty/skill diff --git a/ui-ux-pro-max/cli/assets/data/catalog-summary.json b/ui-ux-pro-max/cli/assets/data/catalog-summary.json index 4a8c7643..0728f827 100644 --- a/ui-ux-pro-max/cli/assets/data/catalog-summary.json +++ b/ui-ux-pro-max/cli/assets/data/catalog-summary.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "verifiedAt": "2026-08-13", + "verifiedAt": "2026-08-26", "counts": { "styles": { "total": 88, diff --git a/ui-ux-pro-max/cli/assets/scripts/tests/test_catalog_summary_line_endings.py b/ui-ux-pro-max/cli/assets/scripts/tests/test_catalog_summary_line_endings.py new file mode 100644 index 00000000..056c8e5e --- /dev/null +++ b/ui-ux-pro-max/cli/assets/scripts/tests/test_catalog_summary_line_endings.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""The catalog snapshot must not depend on the checkout's line endings. + +Regression test for bd19ab9 (#462), where catalog-summary.json was regenerated +on a CRLF checkout. Every recorded sha256 was the CRLF hash of the source file, +so `verify:data` failed on every LF platform, including CI. +""" + +import hashlib +import importlib.util +import json +import shutil +import tempfile +import unittest +from pathlib import Path + +REPO = next( + parent for parent in Path(__file__).resolve().parents + if (parent / "scripts" / "generate-catalog-summary.py").is_file() +) +DATA = REPO / "src/ui-ux-pro-max/data" +SNAPSHOT_FILES = ( + "google-fonts.csv", + "google-font-licenses.json", + "icons.csv", + "phosphor-icons-upstream.json", +) + + +def _load_generator(): + path = REPO / "scripts" / "generate-catalog-summary.py" + spec = importlib.util.spec_from_file_location("generate_catalog_summary", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class CatalogSummaryLineEndingsTest(unittest.TestCase): + def test_digest_is_identical_for_lf_and_crlf(self): + digest = _load_generator().digest + with tempfile.TemporaryDirectory() as tmp: + lf = Path(tmp) / "lf.csv" + crlf = Path(tmp) / "crlf.csv" + lf.write_bytes(b"id,name\n1,alpha\n2,beta\n") + crlf.write_bytes(b"id,name\r\n1,alpha\r\n2,beta\r\n") + self.assertEqual( + digest(lf), digest(crlf), + "snapshot hashes must not change with the checkout's line endings", + ) + + def test_committed_snapshot_matches_normalized_sources(self): + summary = json.loads((DATA / "catalog-summary.json").read_text(encoding="utf-8")) + for name in SNAPSHOT_FILES: + expected = hashlib.sha256( + (DATA / name).read_bytes().replace(b"\r\n", b"\n") + ).hexdigest() + self.assertEqual( + summary["snapshots"][name]["sha256"], expected, + f"{name}: committed snapshot hash does not match the LF-normalized source", + ) + + def test_crlf_checkout_produces_the_committed_hashes(self): + """Simulate a Windows checkout: the recorded hashes must still validate.""" + digest = _load_generator().digest + summary = json.loads((DATA / "catalog-summary.json").read_text(encoding="utf-8")) + with tempfile.TemporaryDirectory() as tmp: + for name in SNAPSHOT_FILES: + crlf_copy = Path(tmp) / name + raw = (DATA / name).read_bytes().replace(b"\r\n", b"\n") + crlf_copy.write_bytes(raw.replace(b"\n", b"\r\n")) + self.assertEqual( + digest(crlf_copy), summary["snapshots"][name]["sha256"], + f"{name}: a CRLF checkout would record a different hash", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/ui-ux-pro-max/cli/assets/scripts/tests/test_skill_script_paths.py b/ui-ux-pro-max/cli/assets/scripts/tests/test_skill_script_paths.py new file mode 100644 index 00000000..36514964 --- /dev/null +++ b/ui-ux-pro-max/cli/assets/scripts/tests/test_skill_script_paths.py @@ -0,0 +1,82 @@ +"""Every script invocation in the shipped skill markdown resolves from the skill directory. + +Regression test for #474. The sub-skills ship in two copies (.claude/skills// +for the plugin, cli/assets/skills// for CLI installs) and land in layouts where +neither the project root nor ~/.claude/skills/ is a valid anchor: the plugin cache, a +project's .claude/skills/, ~/.claude/skills/ (--global), or a manual copy. The one anchor +that exists in all of them is the skill's own directory, so documented commands use +`scripts/` for the skill's own scripts and `..//scripts/` for a +sibling sub-skill (the sub-skills are always installed side by side). + +This test extracts every `python|python3|node|bash ` invocation from every +markdown file under both trees and asserts that the path is skill-relative and names a +file that ships. The core skill's `${CLAUDE_PLUGIN_ROOT}/.claude/skills/...` form is +resolved against the repository root, which is what that variable denotes under a +plugin install - and accepted only in that file, because the sub-skills also ship +through the CLI, where the variable does not exist. The grep-based path contract in check-asset-sync.yml is the negative +side (no home-, project- or variable-rooted paths anywhere, code included); this is +the positive side (every documented invocation points at a real file). +""" + +import re +import unittest +from pathlib import Path + +REPO = next( + parent for parent in Path(__file__).resolve().parents + if (parent / "scripts" / "generate-catalog-summary.py").is_file() +) +SKILL_TREES = ("cli/assets/skills", ".claude/skills") +# The only file that may use the plugin-root form: hand-authored for the plugin install +# and not shipped by the CLI (sync-assets.mjs mirrors data/ and scripts/, never SKILL.md). +# (Built from segments: the path contract in check-asset-sync.yml scans this file too.) +PLUGIN_ONLY_FILE = Path(".claude") / "skills" / "ui-ux-pro-max" / "SKILL.md" +INVOCATION = re.compile(r'(? 3 and parts[2] == "scripts" and (skill_dir.parent / parts[1]).is_dir(): + return skill_dir.parent / parts[1] / "/".join(parts[2:]), None + return None, "a sibling invocation must be ..//scripts/ and the sibling must ship" + return None, "not skill-relative (expected scripts/ or ..//scripts/)" + + +class SkillScriptPathsTest(unittest.TestCase): + def test_every_shipped_markdown_invocation_resolves_from_the_skill_directory(self): + problems, seen = [], 0 + for skill_dir, md, lineno, path in shipped_invocations(): + seen += 1 + target, reason = resolve(skill_dir, md, path) + if reason is None and not target.is_file(): + reason = f"no such file: {target}" + if reason: + problems.append(f"{md.relative_to(REPO)}:{lineno}: {path} -- {reason}") + # Guard against a silently broken extractor: the two trees carry well over + # a hundred documented invocations between them. + self.assertGreater(seen, 100, f"extractor found only {seen} invocations") + self.assertEqual(problems, [], "\n" + "\n".join(problems)) + + +if __name__ == "__main__": + unittest.main() diff --git a/ui-ux-pro-max/cli/assets/scripts/validate_data.py b/ui-ux-pro-max/cli/assets/scripts/validate_data.py index c7797cd9..8b6c7941 100644 --- a/ui-ux-pro-max/cli/assets/scripts/validate_data.py +++ b/ui-ux-pro-max/cli/assets/scripts/validate_data.py @@ -663,7 +663,11 @@ def _check_catalog_summary(summary, licenses, phosphor, problems): problems.append(f"[catalog:summary] stale count for {key}") snapshots = summary.get("snapshots") if isinstance(summary.get("snapshots"), dict) else {} for name in ("google-fonts.csv", "google-font-licenses.json", "icons.csv", "phosphor-icons-upstream.json"): - digest = hashlib.sha256((DATA_DIR / name).read_bytes()).hexdigest() + # Line endings are normalized so the check matches + # generate-catalog-summary.py on CRLF checkouts too. + digest = hashlib.sha256( + (DATA_DIR / name).read_bytes().replace(b"\r\n", b"\n") + ).hexdigest() if snapshots.get(name) != {"sha256": digest}: problems.append(f"[catalog:summary] stale snapshot for {name}") policy = summary.get("promotionPolicy") diff --git a/ui-ux-pro-max/cli/assets/skills/banner-design/SKILL.md b/ui-ux-pro-max/cli/assets/skills/banner-design/SKILL.md index ee935a5c..79fa5ee1 100644 --- a/ui-ux-pro-max/cli/assets/skills/banner-design/SKILL.md +++ b/ui-ux-pro-max/cli/assets/skills/banner-design/SKILL.md @@ -1,6 +1,6 @@ --- name: banner-design -description: "Design banners for social media, ads, website heroes, creative assets, and print. Multiple art direction options with AI-generated visuals. Actions: design, create, generate banner. Platforms: Facebook, Twitter/X, LinkedIn, YouTube, Instagram, Google Display, website hero, print. Styles: minimalist, gradient, bold typography, photo-based, illustrated, geometric, retro, glassmorphism, 3D, neon, duotone, editorial, collage. Uses ui-ux-pro-max, frontend-design, ai-artist, ai-multimodal skills." +description: "Design banners for social media, ads, website heroes, creative assets, and print. Multiple art direction options with optional generated or supplied visuals. Actions: design, create, generate banner. Platforms: Facebook, Twitter/X, LinkedIn, YouTube, Instagram, Google Display, website hero, print. Styles: minimalist, gradient, bold typography, photo-based, illustrated, geometric, retro, glassmorphism, 3D, neon, duotone, editorial, collage." argument-hint: "[platform] [style] [dimensions]" license: MIT metadata: @@ -10,7 +10,7 @@ metadata: # Banner Design - Multi-Format Creative Banner System -Design banners across social, ads, web, and print formats. Generates multiple art direction options per request with AI-powered visual elements. This skill handles banner design only. Does NOT handle video editing, full website design, or print production. +Design banners across social, ads, web, and print formats. Generate multiple art direction options with CSS-built, user-supplied, or optionally generated visual elements. This skill handles banner design only. It does not handle video editing, full website design, or print production. ## When to Activate @@ -21,9 +21,9 @@ Design banners across social, ads, web, and print formats. Generates multiple ar - Event/print banner design - Creative asset generation for campaigns -## Prerequisites +## Available Resources -**Python:** This skill uses Python scripts. On Windows, use `python` instead of `python3` (e.g., `python scripts/search.py` instead of `python3 scripts/search.py`). +This workflow is self-contained: it requires no sibling skills or skill-relative scripts. Use `references/banner-sizes-and-styles.md` for the bundled size, safe-zone, and art-direction guidance. Browser research, image generation, and screenshot tooling are optional capabilities; when unavailable, use supplied assets, CSS-built visuals, and the runtime's standard preview or capture workflow. ## Workflow @@ -33,95 +33,44 @@ Collect via AskUserQuestion: 1. **Purpose** — social cover, ad banner, website hero, print, or creative asset? 2. **Platform/size** — which platform or custom dimensions? 3. **Content** — headline, subtext, CTA, logo placement? -4. **Brand** — existing brand guidelines? (check `docs/brand-guidelines.md`) +4. **Brand** — existing brand guidelines, logo files, colors, or typography? 5. **Style preference** — any art direction? (show style options if unsure) 6. **Quantity** — how many options to generate? (default: 3) ### Step 2: Research & Art Direction -1. Activate `ui-ux-pro-max` skill for design intelligence -2. Use Chrome browser to research Pinterest for design references: - ``` - Navigate to pinterest.com → search "[purpose] banner design [style]" - Screenshot 3-5 reference pins for art direction inspiration - ``` -3. Select 2-3 complementary art direction styles from references: - `references/banner-sizes-and-styles.md` +1. Read `references/banner-sizes-and-styles.md` for the target format, safe zone, and suitable styles. +2. If browser research is available and permitted, collect 3–5 references for composition and art-direction inspiration. Otherwise, work from the bundled reference and any examples supplied by the user. +3. Select 2–3 complementary art directions and state how each supports the banner's purpose. ### Step 3: Design & Generate Options For each art direction option: -1. **Create HTML/CSS banner** using `frontend-design` skill - - Use exact platform dimensions from size reference - - Apply safe zone rules (critical content in central 70-80%) - - Max 2 typefaces, single CTA, 4.5:1 contrast ratio - - Inject brand context via `inject-brand-context.cjs` +1. **Create the banner in HTML/CSS** + - Use the exact platform dimensions from the size reference + - Apply safe-zone rules (critical content in the central 70–80%) + - Use at most 2 typefaces, a single CTA, and text contrast of at least 4.5:1 + - Apply the user's supplied logo, colors, typography, and imagery; do not invent brand rules -2. **Generate visual elements** with `ai-artist` + `ai-multimodal` skills +2. **Choose a visual source** + - Prefer user-supplied or appropriately licensed assets when provided + - Use gradients, geometric forms, type, and other CSS-built visuals for a dependency-free result + - If the runtime provides an authorized image-generation capability, it may generate a background or illustration at the target aspect ratio + - Keep generated visual prompts free of text, letters, and words so final copy remains editable and accessible in HTML - **a) Search prompt inspiration** (6000+ examples in ai-artist): - ```bash - python3 .claude/skills/ai-artist/scripts/search.py "" - ``` - - **b) Generate with Standard model** (fast, good for backgrounds/patterns): - ```bash - .claude/skills/.venv/bin/python3 .claude/skills/ai-multimodal/scripts/gemini_batch_process.py \ - --task generate --model gemini-2.5-flash-image \ - --prompt "" --aspect-ratio \ - --size 2K --output assets/banners/ - ``` - - **c) Generate with Pro model** (4K, complex illustrations/hero visuals): - ```bash - .claude/skills/.venv/bin/python3 .claude/skills/ai-multimodal/scripts/gemini_batch_process.py \ - --task generate --model gemini-3-pro-image-preview \ - --prompt "" --aspect-ratio \ - --size 4K --output assets/banners/ - ``` - - **When to use which model:** - | Use Case | Model | Quality | - |----------|-------|---------| - | Backgrounds, gradients, patterns | Standard (Flash) | 2K, fast | - | Hero illustrations, product shots | Pro | 4K, detailed | - | Photorealistic scenes, complex art | Pro | 4K, best quality | - | Quick iterations, A/B variants | Standard (Flash) | 2K, fast | - - **Aspect ratios:** `1:1`, `16:9`, `9:16`, `3:4`, `4:3`, `2:3`, `3:2` - Match to platform - e.g., Twitter header = `3:1` (use `3:2` closest), Instagram story = `9:16` - - **Pro model prompt tips** (see `ai-artist` references/nano-banana-pro-examples.md): - - Be descriptive: style, lighting, mood, composition, color palette - - Include art direction: "minimalist flat design", "cyberpunk neon", "editorial photography" - - Specify no-text: "no text, no letters, no words" (text overlaid in HTML step) - -3. **Compose final banner** — overlay text, CTA, logo on generated visual in HTML/CSS +3. **Compose the final banner** — overlay the headline, supporting copy, CTA, and logo in HTML/CSS, then verify hierarchy, safe zones, contrast, and crop behavior at the exact target size ### Step 4: Export Banners to Images -After designing HTML banners, export each to PNG using `chrome-devtools` skill: +After designing the HTML banners: -1. **Serve HTML files** via local server (python http.server or similar) -2. **Screenshot each banner** at exact platform dimensions: - ```bash - # Export banner to PNG at exact dimensions - node .claude/skills/chrome-devtools/scripts/screenshot.js \ - --url "http://localhost:8765/banner-01-minimalist.html" \ - --width 1500 --height 500 \ - --output "assets/banners/{campaign}/{variant}-{size}.png" - ``` -3. **Auto-compress** if >5MB (Sharp compression built-in): - ```bash - # With custom max size threshold - node .claude/skills/chrome-devtools/scripts/screenshot.js \ - --url "http://localhost:8765/banner-02-gradient.html" \ - --width 1500 --height 500 --max-size 3 \ - --output "assets/banners/{campaign}/{variant}-{size}.png" - ``` +1. Preview each banner in an available browser at the exact target viewport. +2. Capture the banner element as PNG with the runtime's standard browser or screenshot capability. If capture is unavailable, deliver the HTML/CSS source and clearly mark PNG export as pending rather than naming an uninstalled tool. +3. Verify the exported pixel dimensions, safe-zone crop, font loading, and image quality. +4. If an exported file exceeds the platform limit, use an available image optimizer or reduce image quality and dimensions within the platform specification. -**Output path convention** (per `assets-organizing` skill): +**Output path convention:** ``` assets/banners/{campaign}/ ├── minimalist-1500x500.png @@ -139,7 +88,7 @@ assets/banners/{campaign}/ Present all exported images side-by-side. For each option show: - Art direction style name -- Exported PNG preview (use `ai-multimodal` skill to display if needed) +- Exported PNG preview, or an HTML/CSS preview when image capture is unavailable - Key design rationale - File path & dimensions @@ -185,7 +134,7 @@ Full 22 styles: `references/banner-sizes-and-styles.md` - **Typography**: max 2 fonts, min 16px body, ≥32px headline - **Text ratio**: under 20% for ads (Meta penalizes heavy text) - **Print**: 300 DPI, CMYK, 3-5mm bleed -- **Brand**: always inject via `inject-brand-context.cjs` +- **Brand**: apply only supplied, verified brand guidance and assets ## Security diff --git a/ui-ux-pro-max/cli/assets/skills/brand/SKILL.md b/ui-ux-pro-max/cli/assets/skills/brand/SKILL.md index 336e8ef9..48d912f5 100644 --- a/ui-ux-pro-max/cli/assets/skills/brand/SKILL.md +++ b/ui-ux-pro-max/cli/assets/skills/brand/SKILL.md @@ -20,6 +20,10 @@ Brand identity, voice, messaging, asset management, and consistency frameworks. - Asset organization, naming, and approval - Color palette management and typography specs +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## Quick Start **Inject brand context into prompts:** diff --git a/ui-ux-pro-max/cli/assets/skills/brand/references/approval-checklist.md b/ui-ux-pro-max/cli/assets/skills/brand/references/approval-checklist.md index ff05bacb..0ce4bb69 100644 --- a/ui-ux-pro-max/cli/assets/skills/brand/references/approval-checklist.md +++ b/ui-ux-pro-max/cli/assets/skills/brand/references/approval-checklist.md @@ -157,7 +157,7 @@ The `validate-asset.cjs` script can auto-check: - Naming convention - Basic metadata -Run: `node .claude/skills/brand/scripts/validate-asset.cjs ` +Run: `node scripts/validate-asset.cjs ` ## Archival diff --git a/ui-ux-pro-max/cli/assets/skills/brand/references/update.md b/ui-ux-pro-max/cli/assets/skills/brand/references/update.md index 4a92438e..25ed7f31 100644 --- a/ui-ux-pro-max/cli/assets/skills/brand/references/update.md +++ b/ui-ux-pro-max/cli/assets/skills/brand/references/update.md @@ -46,7 +46,7 @@ Edit `docs/brand-guidelines.md`: Run the sync script: ```bash -node .claude/skills/brand/scripts/sync-brand-to-tokens.cjs +node scripts/sync-brand-to-tokens.cjs ``` This will: @@ -58,7 +58,7 @@ This will: Confirm all files are updated: ```bash # Check brand context extraction -node .claude/skills/brand/scripts/inject-brand-context.cjs --json | head -30 +node scripts/inject-brand-context.cjs --json | head -30 # Check CSS variables grep "primary" assets/design-tokens.css | head -5 diff --git a/ui-ux-pro-max/cli/assets/skills/brand/scripts/extract-colors.cjs b/ui-ux-pro-max/cli/assets/skills/brand/scripts/extract-colors.cjs index a2ec2b43..73aa6d4c 100644 --- a/ui-ux-pro-max/cli/assets/skills/brand/scripts/extract-colors.cjs +++ b/ui-ux-pro-max/cli/assets/skills/brand/scripts/extract-colors.cjs @@ -287,11 +287,7 @@ function main() { "1. Run the ImageMagick command to extract colors:", ` ${generateImageMagickCommand(resolvedPath)}`, "", - "2. Or use the ai-multimodal skill:", - ` python .claude/skills/ai-multimodal/scripts/gemini_batch_process.py \\`, - ` --files "${resolvedPath}" \\`, - ` --task analyze \\`, - ` --prompt "Extract the 10 most dominant colors as hex values"`, + "2. Or use an image-analysis skill (e.g. ai-multimodal, if installed) to extract the 10 most dominant colors as hex values", "", "3. Then compare extracted colors against brand palette", ], diff --git a/ui-ux-pro-max/cli/assets/skills/brand/scripts/sync-brand-to-tokens.cjs b/ui-ux-pro-max/cli/assets/skills/brand/scripts/sync-brand-to-tokens.cjs index 013fa6ff..1e3b8ce6 100644 --- a/ui-ux-pro-max/cli/assets/skills/brand/scripts/sync-brand-to-tokens.cjs +++ b/ui-ux-pro-max/cli/assets/skills/brand/scripts/sync-brand-to-tokens.cjs @@ -17,7 +17,10 @@ const { execFileSync } = require('child_process'); const BRAND_GUIDELINES = 'docs/brand-guidelines.md'; const DESIGN_TOKENS_JSON = 'assets/design-tokens.json'; const DESIGN_TOKENS_CSS = 'assets/design-tokens.css'; -const GENERATE_TOKENS_SCRIPT = '.claude/skills/design-system/scripts/generate-tokens.cjs'; +// Sibling sub-skill, resolved from this file's location so it works in every +// install context (plugin cache, project or --global CLI install), not only +// when the process runs from a project root that contains .claude/skills/. +const GENERATE_TOKENS_SCRIPT = path.resolve(__dirname, '..', '..', 'design-system', 'scripts', 'generate-tokens.cjs'); /** * Extract color info from brand guidelines markdown @@ -229,7 +232,7 @@ function main() { console.log(`✅ Updated: ${DESIGN_TOKENS_JSON}`); // Regenerate CSS - const generateScript = path.resolve(process.cwd(), GENERATE_TOKENS_SCRIPT); + const generateScript = GENERATE_TOKENS_SCRIPT; if (fs.existsSync(generateScript)) { try { execFileSync('node', [generateScript, '--config', DESIGN_TOKENS_JSON, '-o', DESIGN_TOKENS_CSS], { @@ -240,6 +243,8 @@ function main() { } catch (e) { console.error('⚠️ Failed to regenerate CSS:', e.message); } + } else { + console.warn(`⚠️ design-system sub-skill not found at ${generateScript}; ${DESIGN_TOKENS_CSS} not regenerated`); } console.log('\n✨ Brand sync complete!'); diff --git a/ui-ux-pro-max/cli/assets/skills/brand/scripts/tests/test_sync_brand_to_tokens.py b/ui-ux-pro-max/cli/assets/skills/brand/scripts/tests/test_sync_brand_to_tokens.py index 5e6b1e48..53c54370 100644 --- a/ui-ux-pro-max/cli/assets/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +++ b/ui-ux-pro-max/cli/assets/skills/brand/scripts/tests/test_sync_brand_to_tokens.py @@ -62,6 +62,14 @@ def test_sync_parses_bundled_starter_template(tmp_path): assert primitive["secondary"]["500"]["$value"] == "#8B5CF6" assert primitive["accent"]["500"]["$value"] == "#10B981" + # #474: the sibling design-system script is resolved from this skill's own + # location, so the CSS regeneration must run even though tmp_path has no + # .claude/skills/ tree. Before the fix it was resolved from the working + # directory and silently skipped in every layout but a project install. + assert "Regenerated" in result.stdout, result.stdout + css = tmp_path / "assets" / "design-tokens.css" + assert css.exists() and css.stat().st_size > 0 + def test_reports_missing_guidelines_without_breaking_the_harness(tmp_path): """The missing-guidelines path is the one that breaks a locale-decoded pipe. diff --git a/ui-ux-pro-max/cli/assets/skills/design-system/SKILL.md b/ui-ux-pro-max/cli/assets/skills/design-system/SKILL.md index 4397c7f8..804e5533 100644 --- a/ui-ux-pro-max/cli/assets/skills/design-system/SKILL.md +++ b/ui-ux-pro-max/cli/assets/skills/design-system/SKILL.md @@ -48,6 +48,10 @@ Component (component-specific) --button-bg: var(--color-primary); ``` +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## Quick Start **Generate tokens:** diff --git a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/embed-tokens.cjs b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/embed-tokens.cjs index 419c1047..e677f0cd 100644 --- a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/embed-tokens.cjs +++ b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/embed-tokens.cjs @@ -15,12 +15,16 @@ const path = require('path'); // Find project root (look for assets/design-tokens.css) function findProjectRoot(startDir) { + // Walk up until dirname stops changing: on Windows the root is 'C:\', so a + // `dir !== '/'` guard never terminates. let dir = startDir; - while (dir !== '/') { + for (;;) { if (fs.existsSync(path.join(dir, 'assets', 'design-tokens.css'))) { return dir; } - dir = path.dirname(dir); + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; } return null; } diff --git a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/fetch-background.py b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/fetch-background.py index bcbd357e..08a99707 100644 --- a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/fetch-background.py +++ b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/fetch-background.py @@ -9,10 +9,32 @@ import json import csv import re import sys +import os from pathlib import Path -# Project root relative to this script -PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent +# The skill can be installed outside the project it operates on (user-level +# ~/.claude/skills/, or as a plugin), so the project root cannot be derived from +# this file's location. Resolve it from the working directory instead -- the same +# convention generate-tokens.cjs and validate-tokens.cjs already use via +# process.cwd(). DESIGN_SYSTEM_PROJECT_ROOT overrides it explicitly. +def _find_project_root(): + override = os.environ.get('DESIGN_SYSTEM_PROJECT_ROOT') + if override: + return Path(override).resolve() + start = Path.cwd().resolve() + markers = ( + Path('assets') / 'design-tokens.json', + Path('assets') / 'design-tokens.css', + Path('package.json'), + Path('.git'), + ) + for candidate in (start, *start.parents): + if any((candidate / marker).exists() for marker in markers): + return candidate + return start + + +PROJECT_ROOT = _find_project_root() TOKENS_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.json' BACKGROUNDS_CSV = Path(__file__).parent.parent / 'data' / 'slide-backgrounds.csv' diff --git a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/html-token-validator.py b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/html-token-validator.py index a7224980..2b9c5d08 100644 --- a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/html-token-validator.py +++ b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/html-token-validator.py @@ -15,11 +15,43 @@ Usage: import re import json import sys +import os from pathlib import Path from typing import Dict, List, Tuple, Optional -# Project root relative to this script -PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent +# The skill can be installed outside the project it operates on (user-level +# ~/.claude/skills/, or as a plugin), so the project root cannot be derived from +# this file's location. Resolve it from the working directory instead -- the same +# convention generate-tokens.cjs and validate-tokens.cjs already use via +# process.cwd(). DESIGN_SYSTEM_PROJECT_ROOT overrides it explicitly. +def _find_project_root(): + override = os.environ.get('DESIGN_SYSTEM_PROJECT_ROOT') + if override: + return Path(override).resolve() + start = Path.cwd().resolve() + markers = ( + Path('assets') / 'design-tokens.json', + Path('assets') / 'design-tokens.css', + Path('package.json'), + Path('.git'), + ) + for candidate in (start, *start.parents): + if any((candidate / marker).exists() for marker in markers): + return candidate + return start + + +PROJECT_ROOT = _find_project_root() + +# Force UTF-8 on stdout/stderr: this script prints emoji, which raises +# UnicodeEncodeError on a Windows console (cp1252). Same guard as +# src/ui-ux-pro-max/scripts/search.py. +import io + +if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8': + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +if sys.stderr.encoding and sys.stderr.encoding.lower() != 'utf-8': + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') TOKENS_JSON_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.json' TOKENS_CSS_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.css' diff --git a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/search-slides.py b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/search-slides.py index ff5200ad..fc9ce74b 100644 --- a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/search-slides.py +++ b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/search-slides.py @@ -13,6 +13,16 @@ from slide_search_core import ( get_color_for_emotion, get_background_config ) +# Force UTF-8 on stdout/stderr: this script prints emoji, which raises +# UnicodeEncodeError on a Windows console (cp1252). Same guard as +# src/ui-ux-pro-max/scripts/search.py. +import io + +if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8': + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +if sys.stderr.encoding and sys.stderr.encoding.lower() != 'utf-8': + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') + def format_result(result, domain): """Format a single search result for display""" diff --git a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/tests/test_validate_tokens.py b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/tests/test_validate_tokens.py index bbde4ec8..67542467 100644 --- a/ui-ux-pro-max/cli/assets/skills/design-system/scripts/tests/test_validate_tokens.py +++ b/ui-ux-pro-max/cli/assets/skills/design-system/scripts/tests/test_validate_tokens.py @@ -20,11 +20,15 @@ def _run(tmp_path: Path, css: str) -> subprocess.CompletedProcess: node = shutil.which("node") if not node: pytest.skip("node not available") - (tmp_path / "sample.css").write_text(css) + (tmp_path / "sample.css").write_text(css, encoding="utf-8") return subprocess.run( [node, str(SCRIPT), "--dir", str(tmp_path)], capture_output=True, text=True, + # validate-tokens.cjs prints emoji; without an explicit encoding Python + # decodes the pipe with the locale codec (cp1252 on Windows), which + # raises in the reader thread and leaves result.stdout set to None. + encoding="utf-8", ) diff --git a/ui-ux-pro-max/cli/assets/skills/design/SKILL.md b/ui-ux-pro-max/cli/assets/skills/design/SKILL.md index fadccf4f..16256ad3 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/SKILL.md +++ b/ui-ux-pro-max/cli/assets/skills/design/SKILL.md @@ -1,6 +1,6 @@ --- name: design -description: "Comprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini or Atlas Cloud AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG, Gemini 3.1 Pro), social photos (HTML→screenshot, multi-platform). Actions: design logo, create CIP, generate mockups, build slides, design banner, generate icon, create social photos, social media images, brand identity, design system. Platforms: Facebook, Twitter, LinkedIn, YouTube, Instagram, Pinterest, TikTok, Threads, Google Ads." +description: "Comprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini, Atlas Cloud, or MuAPI AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG, Gemini 3.1 Pro), social photos (HTML→screenshot, multi-platform). Actions: design logo, create CIP, generate mockups, build slides, design banner, generate icon, create social photos, social media images, brand identity, design system. Platforms: Facebook, Twitter, LinkedIn, YouTube, Instagram, Pinterest, TikTok, Threads, Google Ads." argument-hint: "[design-type] [context]" license: MIT metadata: @@ -37,22 +37,27 @@ Unified design skill: brand, tokens, UI, logo, CIP, slides, banners, social phot | Social media images/photos | Social Photos (built-in) | `references/social-photos-design.md` | | SVG icons, icon sets | Icon (built-in) | `references/icon-design.md` | +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## Logo Design (Built-in) -55+ styles, 30 color palettes, 25 industry guides. Gemini Nano Banana models. +55+ styles, 30 color palettes, 25 industry guides. Gemini Nano Banana, Atlas +Cloud, and MuAPI image generation. ### Logo: Generate Design Brief ```bash -python3 ~/.claude/skills/design/scripts/logo/search.py "tech startup modern" --design-brief -p "BrandName" +python3 scripts/logo/search.py "tech startup modern" --design-brief -p "BrandName" ``` ### Logo: Search Styles/Colors/Industries ```bash -python3 ~/.claude/skills/design/scripts/logo/search.py "minimalist clean" --domain style -python3 ~/.claude/skills/design/scripts/logo/search.py "tech professional" --domain color -python3 ~/.claude/skills/design/scripts/logo/search.py "healthcare medical" --domain industry +python3 scripts/logo/search.py "minimalist clean" --domain style +python3 scripts/logo/search.py "tech professional" --domain color +python3 scripts/logo/search.py "healthcare medical" --domain industry ``` ### Logo: Generate with AI @@ -60,9 +65,11 @@ python3 ~/.claude/skills/design/scripts/logo/search.py "healthcare medical" --do **ALWAYS** generate output logo images with white background. ```bash -python3 ~/.claude/skills/design/scripts/logo/generate.py --brand "TechFlow" --style minimalist --industry tech -python3 ~/.claude/skills/design/scripts/logo/generate.py --prompt "coffee shop vintage badge" --style vintage -python3 ~/.claude/skills/design/scripts/logo/generate.py --brand "TechFlow" --provider atlas +python3 scripts/logo/generate.py --brand "TechFlow" --style minimalist --industry tech +python3 scripts/logo/generate.py --prompt "coffee shop vintage badge" --style vintage +python3 scripts/logo/generate.py --brand "TechFlow" --provider atlas +python3 scripts/logo/generate.py --brand "TechFlow" --provider muapi +python3 scripts/logo/generate.py --brand "TechFlow" --provider muapi --muapi-model nano-banana-pro ``` **IMPORTANT:** When scripts fail, try to fix them directly. @@ -76,32 +83,32 @@ After generation, **ALWAYS** ask user about HTML preview via `AskUserQuestion`. ### CIP: Generate Brief ```bash -python3 ~/.claude/skills/design/scripts/cip/search.py "tech startup" --cip-brief -b "BrandName" +python3 scripts/cip/search.py "tech startup" --cip-brief -b "BrandName" ``` ### CIP: Search Domains ```bash -python3 ~/.claude/skills/design/scripts/cip/search.py "business card letterhead" --domain deliverable -python3 ~/.claude/skills/design/scripts/cip/search.py "luxury premium elegant" --domain style -python3 ~/.claude/skills/design/scripts/cip/search.py "hospitality hotel" --domain industry -python3 ~/.claude/skills/design/scripts/cip/search.py "office reception" --domain mockup +python3 scripts/cip/search.py "business card letterhead" --domain deliverable +python3 scripts/cip/search.py "luxury premium elegant" --domain style +python3 scripts/cip/search.py "hospitality hotel" --domain industry +python3 scripts/cip/search.py "office reception" --domain mockup ``` ### CIP: Generate Mockups ```bash # With logo (RECOMMENDED) -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --deliverable "business card" --industry "consulting" +python3 scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --deliverable "business card" --industry "consulting" # Full CIP set -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --industry "consulting" --set +python3 scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --industry "consulting" --set # Pro model (4K text) -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo logo.png --deliverable "business card" --model pro +python3 scripts/cip/generate.py --brand "TopGroup" --logo logo.png --deliverable "business card" --model pro # Without logo -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TechFlow" --deliverable "business card" --no-logo-prompt +python3 scripts/cip/generate.py --brand "TechFlow" --deliverable "business card" --no-logo-prompt ``` Models: `flash` (default, `gemini-2.5-flash-image`), `pro` (`gemini-3-pro-image-preview`) @@ -109,7 +116,7 @@ Models: `flash` (default, `gemini-2.5-flash-image`), `pro` (`gemini-3-pro-image- ### CIP: Render HTML Presentation ```bash -python3 ~/.claude/skills/design/scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images /path/to/cip-output +python3 scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images /path/to/cip-output ``` **Tip:** If no logo exists, use Logo Design section above first. @@ -184,21 +191,21 @@ Load `references/banner-sizes-and-styles.md` for complete sizes and styles refer ### Icon: Generate Single Icon ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "settings gear" --style outlined -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "shopping cart" --style filled --color "#6366F1" -python3 ~/.claude/skills/design/scripts/icon/generate.py --name "dashboard" --category navigation --style duotone +python3 scripts/icon/generate.py --prompt "settings gear" --style outlined +python3 scripts/icon/generate.py --prompt "shopping cart" --style filled --color "#6366F1" +python3 scripts/icon/generate.py --name "dashboard" --category navigation --style duotone ``` ### Icon: Generate Batch Variations ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "cloud upload" --batch 4 --output-dir ./icons +python3 scripts/icon/generate.py --prompt "cloud upload" --batch 4 --output-dir ./icons ``` ### Icon: Multi-size Export ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "user profile" --sizes "16,24,32,48" --output-dir ./icons +python3 scripts/icon/generate.py --prompt "user profile" --sizes "16,24,32,48" --output-dir ./icons ``` ### Icon: Top Styles @@ -304,8 +311,20 @@ python3 --version || python --version ```bash export GEMINI_API_KEY="your-key" # https://aistudio.google.com/apikey pip install google-genai pillow + +# Optional MuAPI provider (no extra Python package required) +export MUAPI_API_KEY="your-key" ``` +MuAPI uses the asynchronous model endpoint and prediction result API. See the +[MuAPI API reference](https://muapi.ai/docs/api-reference) for authentication +and the [nano-banana model contract](https://api.muapi.ai/api/v1/models/nano-banana) +or [nano-banana-pro model contract](https://api.muapi.ai/api/v1/models/nano-banana-pro) +for the current model-specific schemas. The logo generator supports both documented +model slugs and sends their shared required `prompt` plus optional `aspect_ratio` +fields; the Pro model also accepts an optional `resolution` field that this focused +logo workflow leaves at the provider default. + > **Note for Windows:** Use `python` instead of `pip` where needed (e.g., `python -m pip install ...`). ## Integration diff --git a/ui-ux-pro-max/cli/assets/skills/design/references/cip-design.md b/ui-ux-pro-max/cli/assets/skills/design/references/cip-design.md index 81829ed5..40377690 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/references/cip-design.md +++ b/ui-ux-pro-max/cli/assets/skills/design/references/cip-design.md @@ -16,49 +16,49 @@ Corporate Identity Program design with 50+ deliverables, 20 styles, 20 industrie ### CIP Brief (Start Here) ```bash -python3 ~/.claude/skills/design/scripts/cip/search.py "tech startup" --cip-brief -b "BrandName" +python3 scripts/cip/search.py "tech startup" --cip-brief -b "BrandName" ``` ### Search Domains ```bash # Deliverables -python3 ~/.claude/skills/design/scripts/cip/search.py "business card letterhead" --domain deliverable +python3 scripts/cip/search.py "business card letterhead" --domain deliverable # Design styles -python3 ~/.claude/skills/design/scripts/cip/search.py "luxury premium elegant" --domain style +python3 scripts/cip/search.py "luxury premium elegant" --domain style # Industry guidelines -python3 ~/.claude/skills/design/scripts/cip/search.py "hospitality hotel" --domain industry +python3 scripts/cip/search.py "hospitality hotel" --domain industry # Mockup contexts -python3 ~/.claude/skills/design/scripts/cip/search.py "office reception" --domain mockup +python3 scripts/cip/search.py "office reception" --domain mockup ``` ### Generate Mockups ```bash # With logo (RECOMMENDED - uses image editing) -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --deliverable "business card" --industry "consulting" +python3 scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --deliverable "business card" --industry "consulting" # Full CIP set with logo -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --industry "consulting" --set +python3 scripts/cip/generate.py --brand "TopGroup" --logo /path/to/logo.png --industry "consulting" --set # Pro model for 4K text rendering -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TopGroup" --logo logo.png --deliverable "business card" --model pro +python3 scripts/cip/generate.py --brand "TopGroup" --logo logo.png --deliverable "business card" --model pro # Custom deliverables with aspect ratio -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "GreenLeaf" --logo logo.png --industry "organic food" --deliverables "letterhead,packaging,vehicle" --ratio 16:9 +python3 scripts/cip/generate.py --brand "GreenLeaf" --logo logo.png --industry "organic food" --deliverables "letterhead,packaging,vehicle" --ratio 16:9 # Without logo (AI generates interpretation) -python3 ~/.claude/skills/design/scripts/cip/generate.py --brand "TechFlow" --deliverable "business card" --no-logo-prompt +python3 scripts/cip/generate.py --brand "TechFlow" --deliverable "business card" --no-logo-prompt ``` ### Render HTML Presentation ```bash -python3 ~/.claude/skills/design/scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images /path/to/cip-output -python3 ~/.claude/skills/design/scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images ./topgroup-cip --output presentation.html +python3 scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images /path/to/cip-output +python3 scripts/cip/render-html.py --brand "TopGroup" --industry "consulting" --images ./topgroup-cip --output presentation.html ``` ## Models diff --git a/ui-ux-pro-max/cli/assets/skills/design/references/design-routing.md b/ui-ux-pro-max/cli/assets/skills/design/references/design-routing.md index 78f55874..4d745bd2 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/references/design-routing.md +++ b/ui-ux-pro-max/cli/assets/skills/design/references/design-routing.md @@ -164,14 +164,14 @@ Application Code **Brand:** ```bash -node .claude/skills/brand/scripts/inject-brand-context.cjs -node .claude/skills/brand/scripts/validate-asset.cjs +node ../brand/scripts/inject-brand-context.cjs +node ../brand/scripts/validate-asset.cjs ``` **Tokens:** ```bash -node .claude/skills/design-system/scripts/generate-tokens.cjs -c tokens.json -node .claude/skills/design-system/scripts/validate-tokens.cjs -d src/ +node ../design-system/scripts/generate-tokens.cjs -c tokens.json +node ../design-system/scripts/validate-tokens.cjs -d src/ ``` **Components:** diff --git a/ui-ux-pro-max/cli/assets/skills/design/references/icon-design.md b/ui-ux-pro-max/cli/assets/skills/design/references/icon-design.md index db6db01d..961cdacc 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/references/icon-design.md +++ b/ui-ux-pro-max/cli/assets/skills/design/references/icon-design.md @@ -13,29 +13,29 @@ AI-powered SVG icon generation using Gemini 3.1 Pro Preview. 15 styles, 12 categ ### Generate Single Icon ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "settings gear" --style outlined -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "shopping cart" --style filled --color "#6366F1" -python3 ~/.claude/skills/design/scripts/icon/generate.py --name "dashboard" --category navigation --style duotone +python3 scripts/icon/generate.py --prompt "settings gear" --style outlined +python3 scripts/icon/generate.py --prompt "shopping cart" --style filled --color "#6366F1" +python3 scripts/icon/generate.py --name "dashboard" --category navigation --style duotone ``` ### Generate Batch Variations ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "cloud upload" --batch 4 --output-dir ./icons -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "notification bell" --batch 6 --style outlined --output-dir ./icons +python3 scripts/icon/generate.py --prompt "cloud upload" --batch 4 --output-dir ./icons +python3 scripts/icon/generate.py --prompt "notification bell" --batch 6 --style outlined --output-dir ./icons ``` ### Generate Multiple Sizes ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --prompt "user profile" --sizes "16,24,32,48" --output-dir ./icons +python3 scripts/icon/generate.py --prompt "user profile" --sizes "16,24,32,48" --output-dir ./icons ``` ### List Styles/Categories ```bash -python3 ~/.claude/skills/design/scripts/icon/generate.py --list-styles -python3 ~/.claude/skills/design/scripts/icon/generate.py --list-categories +python3 scripts/icon/generate.py --list-styles +python3 scripts/icon/generate.py --list-categories ``` ## CLI Options diff --git a/ui-ux-pro-max/cli/assets/skills/design/references/logo-design.md b/ui-ux-pro-max/cli/assets/skills/design/references/logo-design.md index 3fae93b1..258ae777 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/references/logo-design.md +++ b/ui-ux-pro-max/cli/assets/skills/design/references/logo-design.md @@ -1,13 +1,13 @@ # Logo Design Reference -AI-powered logo design with 55+ styles, 30 color palettes, 25 industry guides. Gemini Nano Banana is the default provider; Atlas Cloud is also available as an explicit opt-in. +AI-powered logo design with 55+ styles, 30 color palettes, 25 industry guides. Gemini Nano Banana is the default provider; Atlas Cloud and MuAPI are also available as explicit opt-in providers. ## Scripts | Script | Purpose | |--------|---------| | `scripts/logo/search.py` | Search styles, colors, industries; generate design briefs | -| `scripts/logo/generate.py` | Generate logos with Gemini Nano Banana or Atlas Cloud | +| `scripts/logo/generate.py` | Generate logos with Gemini Nano Banana, Atlas Cloud, or MuAPI | | `scripts/logo/core.py` | BM25 search engine for logo data | ## Commands @@ -15,20 +15,20 @@ AI-powered logo design with 55+ styles, 30 color palettes, 25 industry guides. G ### Design Brief (Start Here) ```bash -python3 ~/.claude/skills/design/scripts/logo/search.py "tech startup modern" --design-brief -p "BrandName" +python3 scripts/logo/search.py "tech startup modern" --design-brief -p "BrandName" ``` ### Search Domains ```bash # Styles -python3 ~/.claude/skills/design/scripts/logo/search.py "minimalist clean" --domain style +python3 scripts/logo/search.py "minimalist clean" --domain style # Color palettes -python3 ~/.claude/skills/design/scripts/logo/search.py "tech professional" --domain color +python3 scripts/logo/search.py "tech professional" --domain color # Industry guidelines -python3 ~/.claude/skills/design/scripts/logo/search.py "healthcare medical" --domain industry +python3 scripts/logo/search.py "healthcare medical" --domain industry ``` ### Generate Logo @@ -36,12 +36,14 @@ python3 ~/.claude/skills/design/scripts/logo/search.py "healthcare medical" --do **ALWAYS** use white background for output logos. ```bash -python3 ~/.claude/skills/design/scripts/logo/generate.py --brand "TechFlow" --style minimalist --industry tech -python3 ~/.claude/skills/design/scripts/logo/generate.py --prompt "coffee shop vintage badge" --style vintage -python3 ~/.claude/skills/design/scripts/logo/generate.py --brand "TechFlow" --provider atlas +python3 scripts/logo/generate.py --brand "TechFlow" --style minimalist --industry tech +python3 scripts/logo/generate.py --prompt "coffee shop vintage badge" --style vintage +python3 scripts/logo/generate.py --brand "TechFlow" --provider atlas +python3 scripts/logo/generate.py --brand "TechFlow" --provider muapi +python3 scripts/logo/generate.py --brand "TechFlow" --provider muapi --muapi-model nano-banana-pro ``` -Options: `--style`, `--industry`, `--prompt`, `--provider`, `--atlas-model` +Options: `--style`, `--industry`, `--prompt`, `--provider`, `--atlas-model`, `--muapi-model` ## Available Styles @@ -93,4 +95,16 @@ pip install google-genai # Optional Atlas Cloud provider (no extra Python package required) export ATLASCLOUD_API_KEY="your-key" + +# Optional MuAPI provider (no extra Python package required) +export MUAPI_API_KEY="your-key" ``` + +MuAPI uses the asynchronous model endpoint and prediction result API. See the +[MuAPI API reference](https://muapi.ai/docs/api-reference) for authentication +and the [nano-banana model contract](https://api.muapi.ai/api/v1/models/nano-banana) +or [nano-banana-pro model contract](https://api.muapi.ai/api/v1/models/nano-banana-pro) +for the current model-specific schemas. The logo generator supports both documented +model slugs and sends their shared required `prompt` plus optional `aspect_ratio` +fields; the Pro model also accepts an optional `resolution` field that this focused +logo workflow leaves at the provider default. diff --git a/ui-ux-pro-max/cli/assets/skills/design/references/slides-copywriting-formulas.md b/ui-ux-pro-max/cli/assets/skills/design/references/slides-copywriting-formulas.md index ecf2875c..87352fc2 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/references/slides-copywriting-formulas.md +++ b/ui-ux-pro-max/cli/assets/skills/design/references/slides-copywriting-formulas.md @@ -66,10 +66,10 @@ ```bash # Find formula for slide type -python .claude/skills/design-system/scripts/search-slides.py "problem agitation" -d copy +python ../design-system/scripts/search-slides.py "problem agitation" -d copy # Get emotion-appropriate formula -python .claude/skills/design-system/scripts/search-slides.py "urgency cta" -d copy +python ../design-system/scripts/search-slides.py "urgency cta" -d copy ``` ## Quick Reference diff --git a/ui-ux-pro-max/cli/assets/skills/design/references/slides-layout-patterns.md b/ui-ux-pro-max/cli/assets/skills/design/references/slides-layout-patterns.md index e2b3849f..0949ab0f 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/references/slides-layout-patterns.md +++ b/ui-ux-pro-max/cli/assets/skills/design/references/slides-layout-patterns.md @@ -113,10 +113,10 @@ ```bash # Find layout for specific use -python .claude/skills/design-system/scripts/search-slides.py "metrics dashboard" -d layout +python ../design-system/scripts/search-slides.py "metrics dashboard" -d layout # Contextual recommendation -python .claude/skills/design-system/scripts/search-slides.py "traction slide" \ +python ../design-system/scripts/search-slides.py "traction slide" \ --context --position 4 --total 10 ``` diff --git a/ui-ux-pro-max/cli/assets/skills/design/references/slides-strategies.md b/ui-ux-pro-max/cli/assets/skills/design/references/slides-strategies.md index e004fe17..eae5b131 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/references/slides-strategies.md +++ b/ui-ux-pro-max/cli/assets/skills/design/references/slides-strategies.md @@ -76,10 +76,10 @@ Pattern breaks at 1/3 and 2/3 positions create engagement peaks. ```bash # Find strategy by goal -python .claude/skills/design-system/scripts/search-slides.py "investor pitch" -d strategy +python ../design-system/scripts/search-slides.py "investor pitch" -d strategy # Get emotion arc -python .claude/skills/design-system/scripts/search-slides.py "series a funding" -d strategy --json +python ../design-system/scripts/search-slides.py "series a funding" -d strategy --json ``` ## Matching Strategy to Context diff --git a/ui-ux-pro-max/cli/assets/skills/design/scripts/cip/generate.py b/ui-ux-pro-max/cli/assets/skills/design/scripts/cip/generate.py index 0be632f5..e92b2e18 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/scripts/cip/generate.py +++ b/ui-ux-pro-max/cli/assets/skills/design/scripts/cip/generate.py @@ -427,7 +427,10 @@ Image Editing Mode: action = check_logo_required(args.brand, skip_prompt=args.no_logo_prompt) if action == 'generate': print("\n💡 To generate a logo, use the logo-design skill:") - print(f" python ~/.claude/skills/design/scripts/logo/generate.py --brand \"{args.brand}\" --industry \"{args.industry}\"") + # Resolved from this file so the hint is correct from any cwd and in + # every install layout (plugin cache, project or --global install). + logo_script = Path(__file__).resolve().parents[1] / "logo" / "generate.py" + print(f" python \"{logo_script}\" --brand \"{args.brand}\" --industry \"{args.industry}\"") print("\n Then re-run this command with --logo ") sys.exit(0) elif action == 'exit': diff --git a/ui-ux-pro-max/cli/assets/skills/design/scripts/logo/generate.py b/ui-ux-pro-max/cli/assets/skills/design/scripts/logo/generate.py index 7de5b702..d006c130 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/scripts/logo/generate.py +++ b/ui-ux-pro-max/cli/assets/skills/design/scripts/logo/generate.py @@ -1,12 +1,15 @@ #!/usr/bin/env python3 -"""Logo generation with Gemini or Atlas Cloud. +"""Logo generation with Gemini, Atlas Cloud, or MuAPI. Gemini remains the default provider. Atlas Cloud is opt-in with -``--provider atlas`` and uses its asynchronous image generation API. +``--provider atlas`` and uses its asynchronous image generation API. MuAPI is +opt-in with ``--provider muapi`` and uses its asynchronous image generation API +with the selected model's prompt/aspect-ratio contract. Models: - Nano Banana (default): gemini-2.5-flash-image - fast, high-volume, low-latency - Nano Banana Pro (--pro): gemini-3-pro-image-preview - professional quality, advanced reasoning +- MuAPI Nano Banana (--provider muapi): nano-banana - hosted asynchronous image generation Usage: python generate.py --prompt "tech startup logo minimalist blue" @@ -14,6 +17,8 @@ Usage: python generate.py --brand "TechFlow" --industry tech --style minimalist python generate.py --brand "TechFlow" --pro # Use Nano Banana Pro model python generate.py --brand "TechFlow" --provider atlas + python generate.py --brand "TechFlow" --provider muapi + python generate.py --brand "TechFlow" --provider muapi --muapi-model nano-banana-pro Batch mode (generates multiple variants): python generate.py --brand "Unikorn" --batch 9 --output-dir ./logos --pro @@ -57,6 +62,7 @@ load_env() # ============ CONFIGURATION ============ GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY") ATLASCLOUD_API_KEY = os.environ.get("ATLASCLOUD_API_KEY") +MUAPI_API_KEY = os.environ.get("MUAPI_API_KEY") # Gemini "Nano Banana" model configurations for image generation GEMINI_FLASH = "gemini-2.5-flash-image" # Nano Banana: fast, high-volume, low-latency @@ -65,9 +71,14 @@ GEMINI_PRO = "gemini-3-pro-image-preview" # Nano Banana Pro: professional quali # Atlas Cloud model validated against the live model catalog and schema. ATLAS_MODEL = "google/nano-banana-2-lite/text-to-image" ATLAS_API_BASE = "https://api.atlascloud.ai/api/v1" -HTTP_USER_AGENT = "ui-ux-pro-max/2.5 (Atlas Cloud logo provider)" +MUAPI_MODEL = "nano-banana" +MUAPI_MODELS = ("nano-banana", "nano-banana-pro") +MUAPI_API_BASE = "https://api.muapi.ai/api/v1" +HTTP_USER_AGENT = "ui-ux-pro-max/2.5 (logo generation)" ATLAS_POLL_INTERVAL = 2 ATLAS_MAX_POLLS = 90 +MUAPI_POLL_INTERVAL = 2 +MUAPI_MAX_POLLS = 90 # Supported aspect ratios ASPECT_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4"] @@ -156,13 +167,13 @@ def _validate_public_https_url(url): or parsed.username or parsed.password ): - raise ValueError("Atlas Cloud returned an invalid media URL") + raise ValueError("Provider returned an invalid media URL") hostname = parsed.hostname.lower().rstrip(".") if hostname == "localhost" or hostname.endswith( (".localhost", ".local", ".internal") ): - raise ValueError("Atlas Cloud media URL used a local hostname") + raise ValueError("Provider media URL used a local hostname") try: ip = ipaddress.ip_address(hostname) @@ -170,17 +181,26 @@ def _validate_public_https_url(url): return else: if not ip.is_global: - raise ValueError("Atlas Cloud media URL used a non-public address") + raise ValueError("Provider media URL used a non-public address") -def _json_request(url, api_key, method="GET", payload=None): +def _json_request( + url, api_key, method="GET", payload=None, api_key_header="Authorization" +): + if api_key_header == "Authorization": + auth_value = f"Bearer {api_key}" + elif api_key_header == "x-api-key": + auth_value = api_key + else: + raise ValueError("Unsupported API key header") + body = json.dumps(payload).encode("utf-8") if payload is not None else None request = Request( url, data=body, method=method, headers={ - "Authorization": f"Bearer {api_key}", + api_key_header: auth_value, "Accept": "application/json", "User-Agent": HTTP_USER_AGENT, **({"Content-Type": "application/json"} if body is not None else {}), @@ -192,10 +212,10 @@ def _json_request(url, api_key, method="GET", payload=None): except HTTPError as exc: detail = exc.read().decode("utf-8", errors="replace") raise RuntimeError( - f"Atlas Cloud request failed ({exc.code}): {detail[:300]}" + f"Provider request failed ({exc.code}): {detail[:300]}" ) from exc except (URLError, TimeoutError, json.JSONDecodeError) as exc: - raise RuntimeError(f"Atlas Cloud request failed: {exc}") from exc + raise RuntimeError(f"Provider request failed: {exc}") from exc def _atlas_prediction_data(response): @@ -210,6 +230,10 @@ def _atlas_prediction_data(response): def _download_atlas_image(url, output_path): + _download_image(url, output_path, "image provider") + + +def _download_image(url, output_path, provider_name): _validate_public_https_url(url) request = Request( url, @@ -222,14 +246,14 @@ def _download_atlas_image(url, output_path): content_type = response.headers.get_content_type() if not content_type.startswith("image/"): raise RuntimeError( - f"Atlas Cloud output is not an image ({content_type})" + f"{provider_name} output is not an image ({content_type})" ) image_data = response.read() except (HTTPError, URLError, TimeoutError) as exc: - raise RuntimeError(f"Unable to download Atlas Cloud image: {exc}") from exc + raise RuntimeError(f"Unable to download {provider_name} image: {exc}") from exc if not image_data: - raise RuntimeError("Atlas Cloud returned an empty image") + raise RuntimeError(f"{provider_name} returned an empty image") with open(output_path, "wb") as output_file: output_file.write(image_data) @@ -281,6 +305,132 @@ def _generate_with_atlas(prompt, output_path, aspect_ratio, api_key, model): raise RuntimeError("Atlas Cloud prediction timed out while polling") +def _muapi_response_objects(response): + """Return the response and common MuAPI envelopes without guessing fields.""" + if not isinstance(response, dict): + raise TypeError("MuAPI returned an invalid response") + + objects = [response] + for key in ("data", "output", "result"): + value = response.get(key) + if isinstance(value, dict) and value not in objects: + objects.append(value) + return objects + + +def _muapi_response_value(response, keys): + for item in _muapi_response_objects(response): + for key in keys: + value = item.get(key) + if value not in (None, ""): + return value + return None + + +def _muapi_error(response): + value = _muapi_response_value(response, ("error", "message", "detail")) + if isinstance(value, str): + return value[:300] + return "MuAPI request failed" + + +def _muapi_result_url(response): + """Return the documented result URL from the creation response.""" + for item in _muapi_response_objects(response): + urls = item.get("urls") + if not isinstance(urls, dict) or "get" not in urls: + continue + + result_url = urls.get("get") + if not isinstance(result_url, str) or not result_url: + raise RuntimeError( + "MuAPI creation response did not include a valid HTTPS result URL" + ) + try: + _validate_public_https_url(result_url) + except ValueError as exc: + raise RuntimeError( + "MuAPI creation response did not include a valid HTTPS result URL" + ) from exc + return result_url + + raise RuntimeError( + "MuAPI creation response did not include a valid HTTPS result URL" + ) + + +def _muapi_output_url(response): + for item in _muapi_response_objects(response): + outputs = item.get("outputs") + if isinstance(outputs, list): + for output in outputs: + if isinstance(output, str) and output.startswith("https://"): + return output + if isinstance(output, dict): + for key in ("url", "image_url"): + value = output.get(key) + if isinstance(value, str) and value.startswith("https://"): + return value + raise RuntimeError("MuAPI completed without an HTTPS image URL") + + +def _download_muapi_image(url, output_path): + _download_image(url, output_path, "MuAPI") + + +def _generate_with_muapi(prompt, output_path, aspect_ratio, api_key, model): + if not api_key: + raise RuntimeError("MUAPI_API_KEY not set") + if model not in MUAPI_MODELS: + raise RuntimeError( + f"Unsupported MuAPI logo model: {model}. " + f"Choose one of: {', '.join(MUAPI_MODELS)}" + ) + + payload = { + "prompt": prompt, + "aspect_ratio": aspect_ratio, + } + response = _json_request( + f"{MUAPI_API_BASE}/{model}", + api_key, + method="POST", + payload=payload, + api_key_header="x-api-key", + ) + request_id = _muapi_response_value(response, ("request_id", "id")) + if not isinstance(request_id, str) or not request_id: + raise RuntimeError("MuAPI did not return a request ID") + result_url = _muapi_result_url(response) + + data = response + for poll_number in range(MUAPI_MAX_POLLS + 1): + status = _muapi_response_value(data, ("status",)) + normalized_status = str(status or "").lower() + if normalized_status in {"completed", "succeeded", "success"}: + _download_muapi_image(_muapi_output_url(data), output_path) + return + if normalized_status in { + "failed", + "error", + "timeout", + "canceled", + "cancelled", + }: + raise RuntimeError(f"MuAPI generation {normalized_status}: {_muapi_error(data)}") + if poll_number == MUAPI_MAX_POLLS: + break + + time.sleep(MUAPI_POLL_INTERVAL) + data = _json_request( + result_url, + api_key, + api_key_header="x-api-key", + ) + + raise RuntimeError("MuAPI prediction timed out while polling") + + def _generate_with_gemini(prompt, output_path, aspect_ratio, use_pro): if not GEMINI_API_KEY: raise RuntimeError("GEMINI_API_KEY not set") @@ -344,8 +494,9 @@ def generate_logo( aspect_ratio=None, provider="gemini", atlas_model=ATLAS_MODEL, + muapi_model=MUAPI_MODEL, ): - """Generate a logo using Gemini or Atlas Cloud image generation. + """Generate a logo using Gemini, Atlas Cloud, or MuAPI image generation. Args: aspect_ratio: Image aspect ratio. Options: "1:1", "16:9", "9:16", "4:3", "3:4" @@ -365,6 +516,8 @@ def generate_logo( if provider == "atlas": model_label = f"Atlas Cloud ({atlas_model})" + elif provider == "muapi": + model_label = f"MuAPI ({muapi_model})" else: model_label = ( "Nano Banana Pro (gemini-3-pro-image-preview)" @@ -386,6 +539,14 @@ def generate_logo( ATLASCLOUD_API_KEY, atlas_model, ) + elif provider == "muapi": + _generate_with_muapi( + full_prompt, + output_path, + ratio, + MUAPI_API_KEY, + muapi_model, + ) else: _generate_with_gemini(full_prompt, output_path, ratio, use_pro) @@ -407,6 +568,7 @@ def generate_batch( aspect_ratio=None, provider="gemini", atlas_model=ATLAS_MODEL, + muapi_model=MUAPI_MODEL, ): """Generate multiple logo variants with different styles""" @@ -430,6 +592,8 @@ def generate_batch( model_label = ( f"Atlas Cloud ({atlas_model})" if provider == "atlas" + else f"MuAPI ({muapi_model})" + if provider == "muapi" else f"Nano Banana {'Pro' if use_pro else 'Flash'}" ) ratio = aspect_ratio if aspect_ratio in ASPECT_RATIOS else DEFAULT_ASPECT_RATIO @@ -466,6 +630,7 @@ def generate_batch( aspect_ratio=aspect_ratio, provider=provider, atlas_model=atlas_model, + muapi_model=muapi_model, ) if result: @@ -487,7 +652,7 @@ def generate_batch( def main(): parser = argparse.ArgumentParser( - description="Generate logos using Gemini or Atlas Cloud" + description="Generate logos using Gemini, Atlas Cloud, or MuAPI" ) parser.add_argument("--prompt", "-p", type=str, help="Logo description prompt") parser.add_argument("--brand", "-b", type=str, help="Brand name") @@ -514,7 +679,7 @@ def main(): ) parser.add_argument( "--provider", - choices=["gemini", "atlas"], + choices=["gemini", "atlas", "muapi"], default="gemini", help="Image provider (default: gemini)", ) @@ -523,6 +688,12 @@ def main(): default=ATLAS_MODEL, help=f"Atlas Cloud image model (default: {ATLAS_MODEL})", ) + parser.add_argument( + "--muapi-model", + choices=MUAPI_MODELS, + default=MUAPI_MODEL, + help=f"MuAPI image model (default: {MUAPI_MODEL})", + ) parser.add_argument( "--aspect-ratio", "-r", @@ -539,8 +710,11 @@ def main(): args = parser.parse_args() - if args.provider == "atlas" and args.pro: - parser.error("--pro is only available with --provider gemini") + if args.provider != "gemini" and args.pro: + parser.error( + "--pro is only available with --provider gemini; " + "use --muapi-model nano-banana-pro for MuAPI" + ) if args.list_styles: print("Available styles:") @@ -574,6 +748,7 @@ def main(): aspect_ratio=args.aspect_ratio, provider=args.provider, atlas_model=args.atlas_model, + muapi_model=args.muapi_model, ) else: generate_logo( @@ -586,6 +761,7 @@ def main(): aspect_ratio=args.aspect_ratio, provider=args.provider, atlas_model=args.atlas_model, + muapi_model=args.muapi_model, ) diff --git a/ui-ux-pro-max/cli/assets/skills/design/scripts/logo/tests/test_generate.py b/ui-ux-pro-max/cli/assets/skills/design/scripts/logo/tests/test_generate.py index 8420d90b..9c2addcd 100644 --- a/ui-ux-pro-max/cli/assets/skills/design/scripts/logo/tests/test_generate.py +++ b/ui-ux-pro-max/cli/assets/skills/design/scripts/logo/tests/test_generate.py @@ -128,5 +128,161 @@ class AtlasGenerationTests(unittest.TestCase): logo_generate._validate_public_https_url("https://assets.local/logo.png") +class MuapiGenerationTests(unittest.TestCase): + @patch.object(logo_generate, "_download_muapi_image") + @patch.object(logo_generate.time, "sleep") + @patch.object(logo_generate, "_json_request") + def test_muapi_submits_once_and_polls_until_completed( + self, json_request, sleep, download + ): + json_request.side_effect = [ + { + "id": "req-123", + "status": "created", + "output": { + "urls": { + "get": "https://api.muapi.ai/api/v1/results/req-123" + } + }, + }, + {"id": "req-123", "status": "processing"}, + { + "id": "req-123", + "status": "completed", + "output": {"outputs": ["https://media.example.com/logo.png"]}, + }, + ] + + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + self.assertEqual(json_request.call_count, 3) + self.assertEqual( + json_request.call_args_list[0], + call( + f"{logo_generate.MUAPI_API_BASE}/nano-banana", + "muapi-key", + method="POST", + payload={"prompt": "logo prompt", "aspect_ratio": "1:1"}, + api_key_header="x-api-key", + ), + ) + self.assertEqual( + json_request.call_args_list[1:], + [ + call( + "https://api.muapi.ai/api/v1/results/req-123", + "muapi-key", + api_key_header="x-api-key", + ), + call( + "https://api.muapi.ai/api/v1/results/req-123", + "muapi-key", + api_key_header="x-api-key", + ), + ], + ) + self.assertEqual(sleep.call_count, 2) + download.assert_called_once_with( + "https://media.example.com/logo.png", "logo.png" + ) + + @patch.object(logo_generate, "_json_request") + def test_muapi_does_not_retry_generation_post(self, json_request): + json_request.side_effect = RuntimeError("network error") + + with self.assertRaisesRegex(RuntimeError, "network error"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + json_request.assert_called_once() + + def test_muapi_requires_key_and_known_model(self): + with self.assertRaisesRegex(RuntimeError, "MUAPI_API_KEY not set"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", None, "nano-banana" + ) + + with self.assertRaisesRegex(RuntimeError, "Unsupported MuAPI logo model"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "unknown-model" + ) + + @patch.object(logo_generate, "build_opener") + def test_muapi_uses_x_api_key_header(self, build_opener): + class Response: + def __enter__(self): + return self + + def __exit__(self, *args): + return None + + @staticmethod + def read(): + return b"{}" + + build_opener.return_value.open.return_value = Response() + + logo_generate._json_request( + "https://api.muapi.ai/api/v1/nano-banana", + "muapi-key", + method="POST", + payload={"prompt": "logo"}, + api_key_header="x-api-key", + ) + + request = build_opener.return_value.open.call_args.args[0] + headers = {key.lower(): value for key, value in request.header_items()} + self.assertEqual(headers["x-api-key"], "muapi-key") + self.assertNotIn("authorization", headers) + + @patch.object(logo_generate, "_json_request") + def test_muapi_reports_failed_prediction(self, json_request): + json_request.side_effect = [ + { + "request_id": "req-123", + "output": { + "urls": { + "get": "https://api.muapi.ai/api/v1/results/req-123" + } + }, + }, + {"status": "failed", "error": "invalid prompt"}, + ] + + with self.assertRaisesRegex(RuntimeError, "invalid prompt"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + @patch.object(logo_generate, "_json_request") + def test_muapi_requires_creation_result_url(self, json_request): + json_request.return_value = {"request_id": "req-123", "status": "created"} + + with self.assertRaisesRegex(RuntimeError, "valid HTTPS result URL"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + json_request.assert_called_once() + + @patch.object(logo_generate, "_json_request") + def test_muapi_rejects_invalid_creation_result_url(self, json_request): + json_request.return_value = { + "request_id": "req-123", + "status": "created", + "output": {"urls": {"get": "http://api.muapi.ai/results/req-123"}}, + } + + with self.assertRaisesRegex(RuntimeError, "valid HTTPS result URL"): + logo_generate._generate_with_muapi( + "logo prompt", "logo.png", "1:1", "muapi-key", "nano-banana" + ) + + json_request.assert_called_once() + + if __name__ == "__main__": unittest.main() diff --git a/ui-ux-pro-max/cli/assets/skills/slides/SKILL.md b/ui-ux-pro-max/cli/assets/skills/slides/SKILL.md index 38750ff1..52d345d1 100644 --- a/ui-ux-pro-max/cli/assets/skills/slides/SKILL.md +++ b/ui-ux-pro-max/cli/assets/skills/slides/SKILL.md @@ -24,6 +24,10 @@ Strategic HTML presentation design with data visualization. |------------|-------------|-----------| | `create` | Create strategic presentation slides | `references/create.md` | +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## References (Knowledge Base) | Topic | File | diff --git a/ui-ux-pro-max/cli/assets/skills/slides/references/copywriting-formulas.md b/ui-ux-pro-max/cli/assets/skills/slides/references/copywriting-formulas.md index ecf2875c..87352fc2 100644 --- a/ui-ux-pro-max/cli/assets/skills/slides/references/copywriting-formulas.md +++ b/ui-ux-pro-max/cli/assets/skills/slides/references/copywriting-formulas.md @@ -66,10 +66,10 @@ ```bash # Find formula for slide type -python .claude/skills/design-system/scripts/search-slides.py "problem agitation" -d copy +python ../design-system/scripts/search-slides.py "problem agitation" -d copy # Get emotion-appropriate formula -python .claude/skills/design-system/scripts/search-slides.py "urgency cta" -d copy +python ../design-system/scripts/search-slides.py "urgency cta" -d copy ``` ## Quick Reference diff --git a/ui-ux-pro-max/cli/assets/skills/slides/references/layout-patterns.md b/ui-ux-pro-max/cli/assets/skills/slides/references/layout-patterns.md index e2b3849f..0949ab0f 100644 --- a/ui-ux-pro-max/cli/assets/skills/slides/references/layout-patterns.md +++ b/ui-ux-pro-max/cli/assets/skills/slides/references/layout-patterns.md @@ -113,10 +113,10 @@ ```bash # Find layout for specific use -python .claude/skills/design-system/scripts/search-slides.py "metrics dashboard" -d layout +python ../design-system/scripts/search-slides.py "metrics dashboard" -d layout # Contextual recommendation -python .claude/skills/design-system/scripts/search-slides.py "traction slide" \ +python ../design-system/scripts/search-slides.py "traction slide" \ --context --position 4 --total 10 ``` diff --git a/ui-ux-pro-max/cli/assets/skills/slides/references/slide-strategies.md b/ui-ux-pro-max/cli/assets/skills/slides/references/slide-strategies.md index e004fe17..eae5b131 100644 --- a/ui-ux-pro-max/cli/assets/skills/slides/references/slide-strategies.md +++ b/ui-ux-pro-max/cli/assets/skills/slides/references/slide-strategies.md @@ -76,10 +76,10 @@ Pattern breaks at 1/3 and 2/3 positions create engagement peaks. ```bash # Find strategy by goal -python .claude/skills/design-system/scripts/search-slides.py "investor pitch" -d strategy +python ../design-system/scripts/search-slides.py "investor pitch" -d strategy # Get emotion arc -python .claude/skills/design-system/scripts/search-slides.py "series a funding" -d strategy --json +python ../design-system/scripts/search-slides.py "series a funding" -d strategy --json ``` ## Matching Strategy to Context diff --git a/ui-ux-pro-max/cli/assets/skills/ui-styling/SKILL.md b/ui-ux-pro-max/cli/assets/skills/ui-styling/SKILL.md index 5824efee..3f86abc8 100644 --- a/ui-ux-pro-max/cli/assets/skills/ui-styling/SKILL.md +++ b/ui-ux-pro-max/cli/assets/skills/ui-styling/SKILL.md @@ -53,6 +53,10 @@ Use when: - Minimal text, maximum visual impact - Systematic patterns and refined aesthetics +## Script Paths + +Script paths in this skill and its `references/` are relative to the directory that contains this SKILL.md, not to the project: `scripts/` is this skill's own `scripts/` folder, and `..//scripts/` is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as `docs/brand-guidelines.md`, `assets/design-tokens.json` or `src/` relative to it. + ## Quick Start ### Component + Styling Setup diff --git a/ui-ux-pro-max/cli/package.json b/ui-ux-pro-max/cli/package.json index 8dc50540..00bb25a5 100755 --- a/ui-ux-pro-max/cli/package.json +++ b/ui-ux-pro-max/cli/package.json @@ -15,14 +15,14 @@ "dev": "bun run src/index.ts", "sync:assets": "node scripts/sync-assets.mjs", "check:assets": "node scripts/sync-assets.mjs --check", - "validate:csv": "cd .. && python3 scripts/validate-csv.py", - "validate:semantic": "cd .. && python3 src/ui-ux-pro-max/scripts/validate_data.py", - "validate:agent-guide": "cd .. && python3 scripts/validate-agent-guide.py", - "validate:catalog-summary": "cd .. && python3 scripts/generate-catalog-summary.py --check", - "test:python": "cd .. && python3 -m unittest discover -s src/ui-ux-pro-max/scripts/tests -p 'test_*.py'", - "evaluate:relevance": "cd .. && python3 scripts/evaluate-relevance.py", - "evaluate:relevance:calibration": "cd .. && python3 scripts/evaluate-relevance.py --split calibration", - "evaluate:relevance:held-out": "cd .. && python3 scripts/evaluate-relevance.py --split held_out", + "validate:csv": "cd .. && node cli/scripts/run-python.mjs scripts/validate-csv.py", + "validate:semantic": "cd .. && node cli/scripts/run-python.mjs src/ui-ux-pro-max/scripts/validate_data.py", + "validate:agent-guide": "cd .. && node cli/scripts/run-python.mjs scripts/validate-agent-guide.py", + "validate:catalog-summary": "cd .. && node cli/scripts/run-python.mjs scripts/generate-catalog-summary.py --check", + "test:python": "cd .. && node cli/scripts/run-python.mjs -m unittest discover -s src/ui-ux-pro-max/scripts/tests -p 'test_*.py'", + "evaluate:relevance": "cd .. && node cli/scripts/run-python.mjs scripts/evaluate-relevance.py", + "evaluate:relevance:calibration": "cd .. && node cli/scripts/run-python.mjs scripts/evaluate-relevance.py --split calibration", + "evaluate:relevance:held-out": "cd .. && node cli/scripts/run-python.mjs scripts/evaluate-relevance.py --split held_out", "smoke:domains": "cd .. && bash scripts/smoke-domains.sh", "smoke:stacks": "cd .. && bash scripts/smoke-stacks.sh", "verify:data": "npm run validate:csv && npm run validate:semantic && npm run validate:agent-guide && npm run validate:catalog-summary && npm run test:python && npm run evaluate:relevance && npm run smoke:domains && npm run smoke:stacks && npm run check:assets", diff --git a/ui-ux-pro-max/cli/scripts/run-python.mjs b/ui-ux-pro-max/cli/scripts/run-python.mjs new file mode 100644 index 00000000..f30d40bb --- /dev/null +++ b/ui-ux-pro-max/cli/scripts/run-python.mjs @@ -0,0 +1,18 @@ +#!/usr/bin/env node + +import { spawnSync } from 'node:child_process'; +import { platform } from 'node:os'; + +const cmd = platform() === 'win32' ? 'python' : 'python3'; +const args = process.argv.slice(2); + +const result = spawnSync(cmd, args, { + stdio: 'inherit', + cwd: process.cwd() +}); + +if (result.error) { + console.error(`Failed to start ${cmd}: ${result.error.message}`); + process.exit(1); +} +process.exit(result.status ?? 0); diff --git a/ui-ux-pro-max/cli/tests/e2e/banner-design-path-contract.spec.ts b/ui-ux-pro-max/cli/tests/e2e/banner-design-path-contract.spec.ts new file mode 100644 index 00000000..0315387d --- /dev/null +++ b/ui-ux-pro-max/cli/tests/e2e/banner-design-path-contract.spec.ts @@ -0,0 +1,66 @@ +import { access, mkdtemp, readFile, rm } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { expect, test } from '@playwright/test'; +import { generatePlatformFiles } from '../../src/utils/template.js'; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../..'); +const skillFiles = [ + '.claude/skills/banner-design/SKILL.md', + 'cli/assets/skills/banner-design/SKILL.md', +]; +const unavailableDependencies = [ + 'frontend-design', + 'ai-artist', + 'ai-multimodal', + 'chrome-devtools', + 'assets-organizing', + 'docs/brand-guidelines.md', + 'scripts/search.py', + 'inject-brand-context.cjs', + 'gemini_batch_process.py', + 'screenshot.js', + 'nano-banana-pro-examples.md', +]; + +function extractLocalReferences(content: string): string[] { + return [...content.matchAll(/`((?:references|scripts)\/[\w./-]+)`/g)].map(match => match[1]); +} + +async function expectSelfContained(skillFile: string): Promise { + const content = await readFile(skillFile, 'utf8'); + + for (const dependency of unavailableDependencies) { + expect(content, dependency).not.toContain(dependency); + } + + const references = extractLocalReferences(content); + expect(references).toContain('references/banner-sizes-and-styles.md'); + for (const reference of references) { + await access(join(dirname(skillFile), reference)); + } +} + +for (const relativeSkillFile of skillFiles) { + test(`${relativeSkillFile} is self-contained`, async () => { + await expectSelfContained(join(repoRoot, relativeSkillFile)); + }); +} + +test('Claude CLI installation preserves the banner path contract', async () => { + const targetDir = await mkdtemp(join(tmpdir(), 'uipro-banner-')); + try { + await generatePlatformFiles(targetDir, 'claude'); + await expectSelfContained(join(targetDir, '.claude/skills/banner-design/SKILL.md')); + } finally { + await rm(targetDir, { recursive: true, force: true }); + } +}); + +test('the bundled banner skill matches the plugin source', async () => { + const [source, bundled] = await Promise.all( + skillFiles.map(skillFile => readFile(join(repoRoot, skillFile), 'utf8')), + ); + expect(bundled).toBe(source); +}); diff --git a/ui-ux-pro-max/scripts/generate-catalog-summary.py b/ui-ux-pro-max/scripts/generate-catalog-summary.py index ffd85ef4..3cf74398 100644 --- a/ui-ux-pro-max/scripts/generate-catalog-summary.py +++ b/ui-ux-pro-max/scripts/generate-catalog-summary.py @@ -20,7 +20,9 @@ def rows(name): def digest(path): - return hashlib.sha256(path.read_bytes()).hexdigest() + # Normalize line endings so the snapshot does not depend on whether the + # working tree was checked out with LF or CRLF. + return hashlib.sha256(path.read_bytes().replace(b"\r\n", b"\n")).hexdigest() def load_json(name): diff --git a/ui-ux-pro-max/src/ui-ux-pro-max/data/catalog-summary.json b/ui-ux-pro-max/src/ui-ux-pro-max/data/catalog-summary.json index 4a8c7643..0728f827 100644 --- a/ui-ux-pro-max/src/ui-ux-pro-max/data/catalog-summary.json +++ b/ui-ux-pro-max/src/ui-ux-pro-max/data/catalog-summary.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "verifiedAt": "2026-08-13", + "verifiedAt": "2026-08-26", "counts": { "styles": { "total": 88, diff --git a/ui-ux-pro-max/src/ui-ux-pro-max/scripts/tests/test_catalog_summary_line_endings.py b/ui-ux-pro-max/src/ui-ux-pro-max/scripts/tests/test_catalog_summary_line_endings.py new file mode 100644 index 00000000..056c8e5e --- /dev/null +++ b/ui-ux-pro-max/src/ui-ux-pro-max/scripts/tests/test_catalog_summary_line_endings.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""The catalog snapshot must not depend on the checkout's line endings. + +Regression test for bd19ab9 (#462), where catalog-summary.json was regenerated +on a CRLF checkout. Every recorded sha256 was the CRLF hash of the source file, +so `verify:data` failed on every LF platform, including CI. +""" + +import hashlib +import importlib.util +import json +import shutil +import tempfile +import unittest +from pathlib import Path + +REPO = next( + parent for parent in Path(__file__).resolve().parents + if (parent / "scripts" / "generate-catalog-summary.py").is_file() +) +DATA = REPO / "src/ui-ux-pro-max/data" +SNAPSHOT_FILES = ( + "google-fonts.csv", + "google-font-licenses.json", + "icons.csv", + "phosphor-icons-upstream.json", +) + + +def _load_generator(): + path = REPO / "scripts" / "generate-catalog-summary.py" + spec = importlib.util.spec_from_file_location("generate_catalog_summary", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class CatalogSummaryLineEndingsTest(unittest.TestCase): + def test_digest_is_identical_for_lf_and_crlf(self): + digest = _load_generator().digest + with tempfile.TemporaryDirectory() as tmp: + lf = Path(tmp) / "lf.csv" + crlf = Path(tmp) / "crlf.csv" + lf.write_bytes(b"id,name\n1,alpha\n2,beta\n") + crlf.write_bytes(b"id,name\r\n1,alpha\r\n2,beta\r\n") + self.assertEqual( + digest(lf), digest(crlf), + "snapshot hashes must not change with the checkout's line endings", + ) + + def test_committed_snapshot_matches_normalized_sources(self): + summary = json.loads((DATA / "catalog-summary.json").read_text(encoding="utf-8")) + for name in SNAPSHOT_FILES: + expected = hashlib.sha256( + (DATA / name).read_bytes().replace(b"\r\n", b"\n") + ).hexdigest() + self.assertEqual( + summary["snapshots"][name]["sha256"], expected, + f"{name}: committed snapshot hash does not match the LF-normalized source", + ) + + def test_crlf_checkout_produces_the_committed_hashes(self): + """Simulate a Windows checkout: the recorded hashes must still validate.""" + digest = _load_generator().digest + summary = json.loads((DATA / "catalog-summary.json").read_text(encoding="utf-8")) + with tempfile.TemporaryDirectory() as tmp: + for name in SNAPSHOT_FILES: + crlf_copy = Path(tmp) / name + raw = (DATA / name).read_bytes().replace(b"\r\n", b"\n") + crlf_copy.write_bytes(raw.replace(b"\n", b"\r\n")) + self.assertEqual( + digest(crlf_copy), summary["snapshots"][name]["sha256"], + f"{name}: a CRLF checkout would record a different hash", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/ui-ux-pro-max/src/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py b/ui-ux-pro-max/src/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py new file mode 100644 index 00000000..36514964 --- /dev/null +++ b/ui-ux-pro-max/src/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py @@ -0,0 +1,82 @@ +"""Every script invocation in the shipped skill markdown resolves from the skill directory. + +Regression test for #474. The sub-skills ship in two copies (.claude/skills// +for the plugin, cli/assets/skills// for CLI installs) and land in layouts where +neither the project root nor ~/.claude/skills/ is a valid anchor: the plugin cache, a +project's .claude/skills/, ~/.claude/skills/ (--global), or a manual copy. The one anchor +that exists in all of them is the skill's own directory, so documented commands use +`scripts/` for the skill's own scripts and `..//scripts/` for a +sibling sub-skill (the sub-skills are always installed side by side). + +This test extracts every `python|python3|node|bash ` invocation from every +markdown file under both trees and asserts that the path is skill-relative and names a +file that ships. The core skill's `${CLAUDE_PLUGIN_ROOT}/.claude/skills/...` form is +resolved against the repository root, which is what that variable denotes under a +plugin install - and accepted only in that file, because the sub-skills also ship +through the CLI, where the variable does not exist. The grep-based path contract in check-asset-sync.yml is the negative +side (no home-, project- or variable-rooted paths anywhere, code included); this is +the positive side (every documented invocation points at a real file). +""" + +import re +import unittest +from pathlib import Path + +REPO = next( + parent for parent in Path(__file__).resolve().parents + if (parent / "scripts" / "generate-catalog-summary.py").is_file() +) +SKILL_TREES = ("cli/assets/skills", ".claude/skills") +# The only file that may use the plugin-root form: hand-authored for the plugin install +# and not shipped by the CLI (sync-assets.mjs mirrors data/ and scripts/, never SKILL.md). +# (Built from segments: the path contract in check-asset-sync.yml scans this file too.) +PLUGIN_ONLY_FILE = Path(".claude") / "skills" / "ui-ux-pro-max" / "SKILL.md" +INVOCATION = re.compile(r'(? 3 and parts[2] == "scripts" and (skill_dir.parent / parts[1]).is_dir(): + return skill_dir.parent / parts[1] / "/".join(parts[2:]), None + return None, "a sibling invocation must be ..//scripts/ and the sibling must ship" + return None, "not skill-relative (expected scripts/ or ..//scripts/)" + + +class SkillScriptPathsTest(unittest.TestCase): + def test_every_shipped_markdown_invocation_resolves_from_the_skill_directory(self): + problems, seen = [], 0 + for skill_dir, md, lineno, path in shipped_invocations(): + seen += 1 + target, reason = resolve(skill_dir, md, path) + if reason is None and not target.is_file(): + reason = f"no such file: {target}" + if reason: + problems.append(f"{md.relative_to(REPO)}:{lineno}: {path} -- {reason}") + # Guard against a silently broken extractor: the two trees carry well over + # a hundred documented invocations between them. + self.assertGreater(seen, 100, f"extractor found only {seen} invocations") + self.assertEqual(problems, [], "\n" + "\n".join(problems)) + + +if __name__ == "__main__": + unittest.main() diff --git a/ui-ux-pro-max/src/ui-ux-pro-max/scripts/validate_data.py b/ui-ux-pro-max/src/ui-ux-pro-max/scripts/validate_data.py index c7797cd9..8b6c7941 100644 --- a/ui-ux-pro-max/src/ui-ux-pro-max/scripts/validate_data.py +++ b/ui-ux-pro-max/src/ui-ux-pro-max/scripts/validate_data.py @@ -663,7 +663,11 @@ def _check_catalog_summary(summary, licenses, phosphor, problems): problems.append(f"[catalog:summary] stale count for {key}") snapshots = summary.get("snapshots") if isinstance(summary.get("snapshots"), dict) else {} for name in ("google-fonts.csv", "google-font-licenses.json", "icons.csv", "phosphor-icons-upstream.json"): - digest = hashlib.sha256((DATA_DIR / name).read_bytes()).hexdigest() + # Line endings are normalized so the check matches + # generate-catalog-summary.py on CRLF checkouts too. + digest = hashlib.sha256( + (DATA_DIR / name).read_bytes().replace(b"\r\n", b"\n") + ).hexdigest() if snapshots.get(name) != {"sha256": digest}: problems.append(f"[catalog:summary] stale snapshot for {name}") policy = summary.get("promotionPolicy")