@jack200714/mafw 4.5.1 → 4.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/bin/mafw.js +16 -14
- package/package.json +1 -7
- package/.opencode/mafw/.backup-v61/parametric/base-skill-manifest.yaml +0 -2
- package/.opencode/mafw/STATUS.md +0 -6
- package/.opencode/mafw/ledger.md +0 -20
- package/.opencode/mafw/memory/.harmonic_index.json +0 -28
- package/.opencode/mafw/memory/.review_queue.json +0 -1
- package/.opencode/mafw/memory/tier3/parametric.json +0 -13
- package/.opencode/mafw/memory/tier3/test-goal.json +0 -19
- package/.opencode/mafw/memory-index.json +0 -6
- package/.opencode/mafw/parametric/base-skill-manifest.yaml +0 -2
- package/.opencode/mafw/waves.json +0 -4
- package/.opencode/opencode.json +0 -3
- package/.opencode/plugins/mafw-plugin.ts +0 -8
- package/.opencode/skills/banner-design/SKILL.md +0 -196
- package/.opencode/skills/banner-design/references/banner-sizes-and-styles.md +0 -118
- package/.opencode/skills/brand/SKILL.md +0 -97
- package/.opencode/skills/brand/references/approval-checklist.md +0 -169
- package/.opencode/skills/brand/references/asset-organization.md +0 -157
- package/.opencode/skills/brand/references/brand-guideline-template.md +0 -140
- package/.opencode/skills/brand/references/color-palette-management.md +0 -186
- package/.opencode/skills/brand/references/consistency-checklist.md +0 -94
- package/.opencode/skills/brand/references/logo-usage-rules.md +0 -185
- package/.opencode/skills/brand/references/messaging-framework.md +0 -85
- package/.opencode/skills/brand/references/typography-specifications.md +0 -214
- package/.opencode/skills/brand/references/update.md +0 -118
- package/.opencode/skills/brand/references/visual-identity.md +0 -96
- package/.opencode/skills/brand/references/voice-framework.md +0 -88
- package/.opencode/skills/brand/scripts/extract-colors.cjs +0 -341
- package/.opencode/skills/brand/scripts/inject-brand-context.cjs +0 -349
- package/.opencode/skills/brand/scripts/sync-brand-to-tokens.cjs +0 -248
- package/.opencode/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +0 -52
- package/.opencode/skills/brand/scripts/validate-asset.cjs +0 -387
- package/.opencode/skills/brand/templates/brand-guidelines-starter.md +0 -275
- package/.opencode/skills/design/SKILL.md +0 -313
- package/.opencode/skills/design/data/cip/deliverables.csv +0 -51
- package/.opencode/skills/design/data/cip/industries.csv +0 -21
- package/.opencode/skills/design/data/cip/mockup-contexts.csv +0 -21
- package/.opencode/skills/design/data/cip/styles.csv +0 -21
- package/.opencode/skills/design/data/icon/styles.csv +0 -16
- package/.opencode/skills/design/data/logo/colors.csv +0 -56
- package/.opencode/skills/design/data/logo/industries.csv +0 -56
- package/.opencode/skills/design/data/logo/styles.csv +0 -56
- package/.opencode/skills/design/references/banner-sizes-and-styles.md +0 -118
- package/.opencode/skills/design/references/cip-deliverable-guide.md +0 -95
- package/.opencode/skills/design/references/cip-design.md +0 -121
- package/.opencode/skills/design/references/cip-prompt-engineering.md +0 -84
- package/.opencode/skills/design/references/cip-style-guide.md +0 -68
- package/.opencode/skills/design/references/design-routing.md +0 -207
- package/.opencode/skills/design/references/icon-design.md +0 -122
- package/.opencode/skills/design/references/logo-color-psychology.md +0 -101
- package/.opencode/skills/design/references/logo-design.md +0 -92
- package/.opencode/skills/design/references/logo-prompt-engineering.md +0 -158
- package/.opencode/skills/design/references/logo-style-guide.md +0 -109
- package/.opencode/skills/design/references/slides-copywriting-formulas.md +0 -84
- package/.opencode/skills/design/references/slides-create.md +0 -4
- package/.opencode/skills/design/references/slides-html-template.md +0 -295
- package/.opencode/skills/design/references/slides-layout-patterns.md +0 -137
- package/.opencode/skills/design/references/slides-strategies.md +0 -94
- package/.opencode/skills/design/references/slides.md +0 -42
- package/.opencode/skills/design/references/social-photos-design.md +0 -329
- package/.opencode/skills/design/scripts/cip/core.py +0 -215
- package/.opencode/skills/design/scripts/cip/generate.py +0 -484
- package/.opencode/skills/design/scripts/cip/render-html.py +0 -424
- package/.opencode/skills/design/scripts/cip/search.py +0 -127
- package/.opencode/skills/design/scripts/icon/generate.py +0 -487
- package/.opencode/skills/design/scripts/logo/core.py +0 -175
- package/.opencode/skills/design/scripts/logo/generate.py +0 -362
- package/.opencode/skills/design/scripts/logo/search.py +0 -114
- package/.opencode/skills/design-system/SKILL.md +0 -244
- package/.opencode/skills/design-system/data/slide-backgrounds.csv +0 -11
- package/.opencode/skills/design-system/data/slide-charts.csv +0 -26
- package/.opencode/skills/design-system/data/slide-color-logic.csv +0 -14
- package/.opencode/skills/design-system/data/slide-copy.csv +0 -26
- package/.opencode/skills/design-system/data/slide-layout-logic.csv +0 -16
- package/.opencode/skills/design-system/data/slide-layouts.csv +0 -26
- package/.opencode/skills/design-system/data/slide-strategies.csv +0 -16
- package/.opencode/skills/design-system/data/slide-typography.csv +0 -15
- package/.opencode/skills/design-system/references/component-specs.md +0 -236
- package/.opencode/skills/design-system/references/component-tokens.md +0 -214
- package/.opencode/skills/design-system/references/primitive-tokens.md +0 -203
- package/.opencode/skills/design-system/references/semantic-tokens.md +0 -215
- package/.opencode/skills/design-system/references/states-and-variants.md +0 -241
- package/.opencode/skills/design-system/references/tailwind-integration.md +0 -251
- package/.opencode/skills/design-system/references/token-architecture.md +0 -224
- package/.opencode/skills/design-system/scripts/embed-tokens.cjs +0 -99
- package/.opencode/skills/design-system/scripts/fetch-background.py +0 -317
- package/.opencode/skills/design-system/scripts/generate-slide.py +0 -770
- package/.opencode/skills/design-system/scripts/generate-tokens.cjs +0 -205
- package/.opencode/skills/design-system/scripts/html-token-validator.py +0 -327
- package/.opencode/skills/design-system/scripts/search-slides.py +0 -218
- package/.opencode/skills/design-system/scripts/slide-token-validator.py +0 -35
- package/.opencode/skills/design-system/scripts/slide_search_core.py +0 -453
- package/.opencode/skills/design-system/scripts/tests/test_validate_tokens.py +0 -48
- package/.opencode/skills/design-system/scripts/validate-tokens.cjs +0 -246
- package/.opencode/skills/design-system/templates/design-tokens-starter.json +0 -143
- package/.opencode/skills/frontend-design/LICENSE.txt +0 -21
- package/.opencode/skills/frontend-design/SKILL.md +0 -115
- package/.opencode/skills/mafw-automation-interview/SKILL.md +0 -43
- package/.opencode/skills/mafw-cli-scanner/SKILL.md +0 -37
- package/.opencode/skills/mafw-compression-verifier/SKILL.md +0 -52
- package/.opencode/skills/mafw-desktop-inspector/SKILL.md +0 -170
- package/.opencode/skills/mafw-execute/SKILL.md +0 -62
- package/.opencode/skills/mafw-gateway-restart/SKILL.md +0 -87
- package/.opencode/skills/mafw-github-scanner/SKILL.md +0 -38
- package/.opencode/skills/mafw-goal/SKILL.md +0 -76
- package/.opencode/skills/mafw-interview/SKILL.md +0 -68
- package/.opencode/skills/mafw-memory-extractor/SKILL.md +0 -65
- package/.opencode/skills/mafw-plan/SKILL.md +0 -58
- package/.opencode/skills/mafw-review/SKILL.md +0 -85
- package/.opencode/skills/runtime-plugin-authoring/SKILL.md +0 -791
- package/.opencode/skills/slides/SKILL.md +0 -40
- package/.opencode/skills/slides/references/copywriting-formulas.md +0 -84
- package/.opencode/skills/slides/references/create.md +0 -4
- package/.opencode/skills/slides/references/html-template.md +0 -295
- package/.opencode/skills/slides/references/layout-patterns.md +0 -137
- package/.opencode/skills/slides/references/slide-strategies.md +0 -94
- package/.opencode/skills/ui-styling/LICENSE.txt +0 -202
- package/.opencode/skills/ui-styling/SKILL.md +0 -324
- package/.opencode/skills/ui-styling/references/canvas-design-system.md +0 -320
- package/.opencode/skills/ui-styling/references/shadcn-accessibility.md +0 -471
- package/.opencode/skills/ui-styling/references/shadcn-components.md +0 -424
- package/.opencode/skills/ui-styling/references/shadcn-theming.md +0 -373
- package/.opencode/skills/ui-styling/references/tailwind-customization.md +0 -483
- package/.opencode/skills/ui-styling/references/tailwind-responsive.md +0 -382
- package/.opencode/skills/ui-styling/references/tailwind-utilities.md +0 -455
- package/.opencode/skills/ui-styling/scripts/requirements.txt +0 -17
- package/.opencode/skills/ui-styling/scripts/shadcn_add.py +0 -308
- package/.opencode/skills/ui-styling/scripts/tailwind_config_gen.py +0 -473
- package/.opencode/skills/ui-styling/scripts/tests/coverage-ui.json +0 -1
- package/.opencode/skills/ui-styling/scripts/tests/requirements.txt +0 -3
- package/.opencode/skills/ui-styling/scripts/tests/test_shadcn_add.py +0 -266
- package/.opencode/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py +0 -394
- package/.opencode/skills/ui-ux-pro-max/SKILL.md +0 -388
- package/.opencode/skills/ui-ux-pro-max/data/app-interface.csv +0 -31
- package/.opencode/skills/ui-ux-pro-max/data/charts.csv +0 -26
- package/.opencode/skills/ui-ux-pro-max/data/colors.csv +0 -193
- package/.opencode/skills/ui-ux-pro-max/data/google-fonts.csv +0 -1924
- package/.opencode/skills/ui-ux-pro-max/data/icons.csv +0 -106
- package/.opencode/skills/ui-ux-pro-max/data/landing.csv +0 -35
- package/.opencode/skills/ui-ux-pro-max/data/motion.csv +0 -17
- package/.opencode/skills/ui-ux-pro-max/data/products.csv +0 -193
- package/.opencode/skills/ui-ux-pro-max/data/react-performance.csv +0 -45
- package/.opencode/skills/ui-ux-pro-max/data/stacks/angular.csv +0 -51
- package/.opencode/skills/ui-ux-pro-max/data/stacks/astro.csv +0 -54
- package/.opencode/skills/ui-ux-pro-max/data/stacks/avalonia.csv +0 -57
- package/.opencode/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
- package/.opencode/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
- package/.opencode/skills/ui-ux-pro-max/data/stacks/javafx.csv +0 -76
- package/.opencode/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
- package/.opencode/skills/ui-ux-pro-max/data/stacks/laravel.csv +0 -51
- package/.opencode/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
- package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -71
- package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
- package/.opencode/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
- package/.opencode/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
- package/.opencode/skills/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
- package/.opencode/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
- package/.opencode/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
- package/.opencode/skills/ui-ux-pro-max/data/stacks/threejs.csv +0 -54
- package/.opencode/skills/ui-ux-pro-max/data/stacks/uno.csv +0 -60
- package/.opencode/skills/ui-ux-pro-max/data/stacks/uwp.csv +0 -56
- package/.opencode/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
- package/.opencode/skills/ui-ux-pro-max/data/stacks/winui.csv +0 -60
- package/.opencode/skills/ui-ux-pro-max/data/stacks/wpf.csv +0 -57
- package/.opencode/skills/ui-ux-pro-max/data/styles.csv +0 -85
- package/.opencode/skills/ui-ux-pro-max/data/typography.csv +0 -75
- package/.opencode/skills/ui-ux-pro-max/data/ui-reasoning.csv +0 -162
- package/.opencode/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
- package/.opencode/skills/ui-ux-pro-max/scripts/core.py +0 -464
- package/.opencode/skills/ui-ux-pro-max/scripts/design_system.py +0 -1479
- package/.opencode/skills/ui-ux-pro-max/scripts/search.py +0 -162
- package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_core.py +0 -134
- package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py +0 -159
- package/.opencode/skills/ui-ux-pro-max/scripts/validate_data.py +0 -114
- package/dist/hooks/bash-python-guide.d.ts +0 -4
- package/dist/hooks/bash-python-guide.d.ts.map +0 -1
- package/dist/hooks/bash-python-guide.js +0 -67
- package/dist/hooks/bash-python-guide.js.map +0 -1
- package/dist/hooks/handoff.d.ts +0 -9
- package/dist/hooks/handoff.d.ts.map +0 -1
- package/dist/hooks/handoff.js +0 -8
- package/dist/hooks/handoff.js.map +0 -1
- package/dist/hooks/hook-manager.d.ts +0 -32
- package/dist/hooks/hook-manager.d.ts.map +0 -1
- package/dist/hooks/hook-manager.js +0 -91
- package/dist/hooks/hook-manager.js.map +0 -1
- package/dist/hooks/llm-after.d.ts +0 -8
- package/dist/hooks/llm-after.d.ts.map +0 -1
- package/dist/hooks/llm-after.js +0 -8
- package/dist/hooks/llm-after.js.map +0 -1
- package/dist/hooks/media-ingest.d.ts +0 -43
- package/dist/hooks/media-ingest.d.ts.map +0 -1
- package/dist/hooks/media-ingest.js +0 -269
- package/dist/hooks/media-ingest.js.map +0 -1
- package/dist/hooks/memory-guide.d.ts +0 -3
- package/dist/hooks/memory-guide.d.ts.map +0 -1
- package/dist/hooks/memory-guide.js +0 -47
- package/dist/hooks/memory-guide.js.map +0 -1
- package/dist/hooks/observation-capture.d.ts +0 -17
- package/dist/hooks/observation-capture.d.ts.map +0 -1
- package/dist/hooks/observation-capture.js +0 -20
- package/dist/hooks/observation-capture.js.map +0 -1
- package/dist/hooks/session-compacting.d.ts +0 -6
- package/dist/hooks/session-compacting.d.ts.map +0 -1
- package/dist/hooks/session-compacting.js +0 -6
- package/dist/hooks/session-compacting.js.map +0 -1
- package/dist/hooks/session-ending.d.ts +0 -20
- package/dist/hooks/session-ending.d.ts.map +0 -1
- package/dist/hooks/session-ending.js +0 -113
- package/dist/hooks/session-ending.js.map +0 -1
- package/dist/hooks/session-recall.d.ts +0 -2
- package/dist/hooks/session-recall.d.ts.map +0 -1
- package/dist/hooks/session-recall.js +0 -103
- package/dist/hooks/session-recall.js.map +0 -1
- package/dist/hooks/session-start.d.ts +0 -9
- package/dist/hooks/session-start.d.ts.map +0 -1
- package/dist/hooks/session-start.js +0 -42
- package/dist/hooks/session-start.js.map +0 -1
- package/dist/hooks/tool-before.d.ts +0 -10
- package/dist/hooks/tool-before.d.ts.map +0 -1
- package/dist/hooks/tool-before.js +0 -6
- package/dist/hooks/tool-before.js.map +0 -1
- package/dist/hooks/tool-executed.d.ts +0 -19
- package/dist/hooks/tool-executed.d.ts.map +0 -1
- package/dist/hooks/tool-executed.js +0 -12
- package/dist/hooks/tool-executed.js.map +0 -1
- package/dist/hooks/user-profile.d.ts +0 -2
- package/dist/hooks/user-profile.d.ts.map +0 -1
- package/dist/hooks/user-profile.js +0 -30
- package/dist/hooks/user-profile.js.map +0 -1
- package/dist/hooks/user-prompt.d.ts +0 -9
- package/dist/hooks/user-prompt.d.ts.map +0 -1
- package/dist/hooks/user-prompt.js +0 -8
- package/dist/hooks/user-prompt.js.map +0 -1
- package/dist/hooks/voice-guide.d.ts +0 -8
- package/dist/hooks/voice-guide.d.ts.map +0 -1
- package/dist/hooks/voice-guide.js +0 -101
- package/dist/hooks/voice-guide.js.map +0 -1
- package/dist/plugin.d.ts +0 -55
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js +0 -342
- package/dist/plugin.js.map +0 -1
- package/dist/tools/add-memory.d.ts +0 -17
- package/dist/tools/add-memory.d.ts.map +0 -1
- package/dist/tools/add-memory.js +0 -60
- package/dist/tools/add-memory.js.map +0 -1
- package/dist/tools/media-ask.d.ts +0 -14
- package/dist/tools/media-ask.d.ts.map +0 -1
- package/dist/tools/media-ask.js +0 -224
- package/dist/tools/media-ask.js.map +0 -1
- package/dist/tools/media-speak.d.ts +0 -16
- package/dist/tools/media-speak.d.ts.map +0 -1
- package/dist/tools/media-speak.js +0 -78
- package/dist/tools/media-speak.js.map +0 -1
- package/dist/tools/media-upload.d.ts +0 -14
- package/dist/tools/media-upload.d.ts.map +0 -1
- package/dist/tools/media-upload.js +0 -155
- package/dist/tools/media-upload.js.map +0 -1
- package/dist/tools/python-exec.d.ts +0 -13
- package/dist/tools/python-exec.d.ts.map +0 -1
- package/dist/tools/python-exec.js +0 -99
- package/dist/tools/python-exec.js.map +0 -1
- package/dist/tools/python-restart.d.ts +0 -11
- package/dist/tools/python-restart.d.ts.map +0 -1
- package/dist/tools/python-restart.js +0 -33
- package/dist/tools/python-restart.js.map +0 -1
- package/dist/utils/circuit-breaker.d.ts +0 -23
- package/dist/utils/circuit-breaker.d.ts.map +0 -1
- package/dist/utils/circuit-breaker.js +0 -75
- package/dist/utils/circuit-breaker.js.map +0 -1
- package/dist/utils/config-loader.d.ts +0 -20
- package/dist/utils/config-loader.d.ts.map +0 -1
- package/dist/utils/config-loader.js +0 -194
- package/dist/utils/config-loader.js.map +0 -1
- package/dist/utils/fallback.d.ts +0 -7
- package/dist/utils/fallback.d.ts.map +0 -1
- package/dist/utils/fallback.js +0 -20
- package/dist/utils/fallback.js.map +0 -1
- package/dist/utils/git.d.ts +0 -24
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.js +0 -71
- package/dist/utils/git.js.map +0 -1
- package/dist/utils/github.d.ts +0 -37
- package/dist/utils/github.d.ts.map +0 -1
- package/dist/utils/github.js +0 -53
- package/dist/utils/github.js.map +0 -1
- package/dist/utils/global-path.d.ts +0 -2
- package/dist/utils/global-path.d.ts.map +0 -1
- package/dist/utils/global-path.js +0 -32
- package/dist/utils/global-path.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -6
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -69
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/obs-capture.d.ts +0 -7
- package/dist/utils/obs-capture.d.ts.map +0 -1
- package/dist/utils/obs-capture.js +0 -49
- package/dist/utils/obs-capture.js.map +0 -1
- package/dist/utils/retry.d.ts +0 -11
- package/dist/utils/retry.d.ts.map +0 -1
- package/dist/utils/retry.js +0 -45
- package/dist/utils/retry.js.map +0 -1
- package/dist/utils/self-wiring.d.ts +0 -13
- package/dist/utils/self-wiring.d.ts.map +0 -1
- package/dist/utils/self-wiring.js +0 -86
- package/dist/utils/self-wiring.js.map +0 -1
- package/dist/utils/state.d.ts +0 -106
- package/dist/utils/state.d.ts.map +0 -1
- package/dist/utils/state.js +0 -166
- package/dist/utils/state.js.map +0 -1
- package/dist/utils/status.d.ts +0 -47
- package/dist/utils/status.d.ts.map +0 -1
- package/dist/utils/status.js +0 -147
- package/dist/utils/status.js.map +0 -1
- package/dist/utils/ttl-map.d.ts +0 -17
- package/dist/utils/ttl-map.d.ts.map +0 -1
- package/dist/utils/ttl-map.js +0 -67
- package/dist/utils/ttl-map.js.map +0 -1
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
UI/UX Pro Max Search - BM25 search engine for UI/UX style guides
|
|
5
|
-
Usage: python search.py "<query>" [--domain <domain>] [--stack <stack>] [--max-results 3]
|
|
6
|
-
python search.py "<query>" --design-system [-p "Project Name"]
|
|
7
|
-
python search.py "<query>" --design-system --persist [-p "Project Name"] --output-dir "<project-root>" [--page "dashboard"]
|
|
8
|
-
python search.py "<query>" --design-system --variance 8 --motion 9 --density 7
|
|
9
|
-
|
|
10
|
-
Domains: style, color, chart, landing, product, ux, typography, google-fonts, icons, gsap, react, web
|
|
11
|
-
Stacks: react, nextjs, vue, svelte, astro, swiftui, react-native, flutter, nuxtjs, nuxt-ui,
|
|
12
|
-
html-tailwind, shadcn, jetpack-compose, threejs, angular, laravel
|
|
13
|
-
|
|
14
|
-
Design dials (1-10, only with --design-system):
|
|
15
|
-
--variance DESIGN_VARIANCE: 1=centered/minimal, 10=bold/asymmetric
|
|
16
|
-
--motion MOTION_INTENSITY: 1=subtle, 10=complex; attaches a GSAP snippet from motion.csv
|
|
17
|
-
--density VISUAL_DENSITY: 1=spacious, 10=dense/dashboard; overrides the spacing scale
|
|
18
|
-
|
|
19
|
-
Persistence (Master + Overrides pattern):
|
|
20
|
-
--persist Save design system to design-system/<project-slug>/MASTER.md
|
|
21
|
-
--output-dir Directory the design-system/ folder is created under (defaults to cwd --
|
|
22
|
-
always pass this explicitly, pointed at the project root)
|
|
23
|
-
--page Also create a page-specific override file in design-system/<project-slug>/pages/
|
|
24
|
-
--force Overwrite an existing MASTER.md (without this, persistence is skipped
|
|
25
|
-
if MASTER.md already exists, so prior design decisions aren't lost)
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
import argparse
|
|
29
|
-
import json as json_module
|
|
30
|
-
import sys
|
|
31
|
-
import io
|
|
32
|
-
from core import CSV_CONFIG, AVAILABLE_STACKS, MAX_RESULTS, UNTRUNCATED_COLS, search, search_stack
|
|
33
|
-
from design_system import generate_design_system
|
|
34
|
-
|
|
35
|
-
# Force UTF-8 for stdout/stderr to handle emojis on Windows (cp1252 default)
|
|
36
|
-
if sys.stdout.encoding and sys.stdout.encoding.lower() != 'utf-8':
|
|
37
|
-
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
38
|
-
if sys.stderr.encoding and sys.stderr.encoding.lower() != 'utf-8':
|
|
39
|
-
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
|
40
|
-
|
|
41
|
-
TRUNCATE_AT = 300
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def format_output(result, full=False):
|
|
45
|
-
"""Format results for Claude consumption (token-optimized)"""
|
|
46
|
-
if "error" in result:
|
|
47
|
-
return f"Error: {result['error']}"
|
|
48
|
-
|
|
49
|
-
output = []
|
|
50
|
-
if result.get("stack"):
|
|
51
|
-
output.append("## UI Pro Max Stack Guidelines")
|
|
52
|
-
output.append(f"**Stack:** {result['stack']} | **Query:** {result['query']}")
|
|
53
|
-
else:
|
|
54
|
-
output.append("## UI Pro Max Search Results")
|
|
55
|
-
domain_note = result['domain']
|
|
56
|
-
if result.get("auto_detected"):
|
|
57
|
-
domain_note += " (auto-detected"
|
|
58
|
-
if result.get("runner_up_domain"):
|
|
59
|
-
domain_note += f", runner-up: {result['runner_up_domain']}"
|
|
60
|
-
domain_note += ")"
|
|
61
|
-
output.append(f"**Domain:** {domain_note} | **Query:** {result['query']}")
|
|
62
|
-
output.append(f"**Source:** {result['file']} | **Found:** {result['count']} results\n")
|
|
63
|
-
|
|
64
|
-
if result['count'] == 0:
|
|
65
|
-
output.append(
|
|
66
|
-
"No matches. This is not a match with an empty value -- the query "
|
|
67
|
-
"did not hit the database. Retry with broader/different keywords "
|
|
68
|
-
"before falling back to general defaults, and say explicitly that "
|
|
69
|
-
"no database match was found if you do fall back."
|
|
70
|
-
)
|
|
71
|
-
suggestions = result.get("suggestions") or []
|
|
72
|
-
if suggestions:
|
|
73
|
-
output.append(f"**Closest known terms:** {', '.join(suggestions)}")
|
|
74
|
-
return "\n".join(output)
|
|
75
|
-
|
|
76
|
-
for i, row in enumerate(result['results'], 1):
|
|
77
|
-
output.append(f"### Result {i}")
|
|
78
|
-
for key, value in row.items():
|
|
79
|
-
value_str = str(value)
|
|
80
|
-
if not full and key not in UNTRUNCATED_COLS and len(value_str) > TRUNCATE_AT:
|
|
81
|
-
value_str = value_str[:TRUNCATE_AT] + "..."
|
|
82
|
-
output.append(f"- **{key}:** {value_str}")
|
|
83
|
-
output.append("")
|
|
84
|
-
|
|
85
|
-
return "\n".join(output)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if __name__ == "__main__":
|
|
89
|
-
parser = argparse.ArgumentParser(description="UI Pro Max Search")
|
|
90
|
-
parser.add_argument("query", help="Search query")
|
|
91
|
-
parser.add_argument("--domain", "-d", choices=list(CSV_CONFIG.keys()), help="Search domain")
|
|
92
|
-
parser.add_argument("--stack", "-s", choices=AVAILABLE_STACKS, help=f"Stack-specific search. Available: {', '.join(AVAILABLE_STACKS)}")
|
|
93
|
-
parser.add_argument("--max-results", "-n", type=int, default=MAX_RESULTS, help="Max results (default: 3)")
|
|
94
|
-
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
95
|
-
parser.add_argument("--full", action="store_true", help="Do not truncate long field values in text output")
|
|
96
|
-
# Design system generation
|
|
97
|
-
parser.add_argument("--design-system", "-ds", action="store_true", help="Generate complete design system recommendation")
|
|
98
|
-
parser.add_argument("--project-name", "-p", type=str, default=None, help="Project name for design system output")
|
|
99
|
-
parser.add_argument("--format", "-f", choices=["ascii", "markdown"], default="ascii", help="Output format for design system (ignored if --json)")
|
|
100
|
-
# Persistence (Master + Overrides pattern)
|
|
101
|
-
parser.add_argument("--persist", action="store_true", help="Save design system to design-system/<project-slug>/MASTER.md (creates hierarchical structure)")
|
|
102
|
-
parser.add_argument("--page", type=str, default=None, help="Create page-specific override file in design-system/<project-slug>/pages/")
|
|
103
|
-
parser.add_argument("--output-dir", "-o", type=str, default=None, help="Output directory for persisted files (default: current directory -- pass this explicitly, pointed at the project root)")
|
|
104
|
-
parser.add_argument("--force", action="store_true", help="Overwrite an existing MASTER.md when persisting (default: skip if it already exists)")
|
|
105
|
-
# Design dials (1-10), only applied with --design-system
|
|
106
|
-
parser.add_argument("--variance", type=int, choices=range(1, 11), metavar="1-10", help="DESIGN_VARIANCE dial: 1=centered/minimal, 10=bold/asymmetric (only with --design-system)")
|
|
107
|
-
parser.add_argument("--motion", type=int, choices=range(1, 11), metavar="1-10", help="MOTION_INTENSITY dial: 1=subtle, 10=complex; pulls a matching GSAP snippet from motion.csv (only with --design-system)")
|
|
108
|
-
parser.add_argument("--density", type=int, choices=range(1, 11), metavar="1-10", help="VISUAL_DENSITY dial: 1=spacious, 10=dense/dashboard; overrides the spacing scale (only with --design-system)")
|
|
109
|
-
|
|
110
|
-
args = parser.parse_args()
|
|
111
|
-
|
|
112
|
-
# Design system takes priority
|
|
113
|
-
if args.design_system:
|
|
114
|
-
result = generate_design_system(
|
|
115
|
-
args.query,
|
|
116
|
-
args.project_name,
|
|
117
|
-
args.format,
|
|
118
|
-
persist=args.persist,
|
|
119
|
-
page=args.page,
|
|
120
|
-
output_dir=args.output_dir,
|
|
121
|
-
variance=args.variance,
|
|
122
|
-
motion=args.motion,
|
|
123
|
-
density=args.density,
|
|
124
|
-
force=args.force,
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
if args.json:
|
|
128
|
-
print(json_module.dumps(
|
|
129
|
-
{"design_system": result["design_system"], "persistence": result["persistence"]},
|
|
130
|
-
indent=2, ensure_ascii=False,
|
|
131
|
-
))
|
|
132
|
-
else:
|
|
133
|
-
print(result["text"])
|
|
134
|
-
|
|
135
|
-
if args.persist:
|
|
136
|
-
persistence = result["persistence"] or {}
|
|
137
|
-
print("\n" + "=" * 60)
|
|
138
|
-
if persistence.get("status") == "skipped_exists":
|
|
139
|
-
print(f"⚠️ {persistence.get('message', 'MASTER.md already exists; not overwritten.')}")
|
|
140
|
-
else:
|
|
141
|
-
ds_dir = persistence.get("design_system_dir", "design-system/<project>")
|
|
142
|
-
print(f"✅ Design system persisted to {ds_dir}/")
|
|
143
|
-
for f in persistence.get("created_files", []):
|
|
144
|
-
print(f" 📄 {f}")
|
|
145
|
-
print("")
|
|
146
|
-
print(f"📖 Usage: When building a page, check {ds_dir}/pages/[page].md first.")
|
|
147
|
-
print(" If it exists, its rules override MASTER.md. Otherwise, use MASTER.md.")
|
|
148
|
-
print("=" * 60)
|
|
149
|
-
# Stack search
|
|
150
|
-
elif args.stack:
|
|
151
|
-
result = search_stack(args.query, args.stack, args.max_results)
|
|
152
|
-
if args.json:
|
|
153
|
-
print(json_module.dumps(result, indent=2, ensure_ascii=False))
|
|
154
|
-
else:
|
|
155
|
-
print(format_output(result, full=args.full))
|
|
156
|
-
# Domain search
|
|
157
|
-
else:
|
|
158
|
-
result = search(args.query, args.domain, args.max_results)
|
|
159
|
-
if args.json:
|
|
160
|
-
print(json_module.dumps(result, indent=2, ensure_ascii=False))
|
|
161
|
-
else:
|
|
162
|
-
print(format_output(result, full=args.full))
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Stdlib-only regression tests for core.py / design_system.py (unittest, not
|
|
5
|
-
pytest -- this project ships with zero external dependencies and the tests
|
|
6
|
-
shouldn't add one).
|
|
7
|
-
|
|
8
|
-
Run with:
|
|
9
|
-
python -m unittest discover -s scripts/tests -v
|
|
10
|
-
or directly:
|
|
11
|
-
python scripts/tests/test_core.py
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import sys
|
|
15
|
-
import tempfile
|
|
16
|
-
import unittest
|
|
17
|
-
from pathlib import Path
|
|
18
|
-
|
|
19
|
-
SCRIPTS_DIR = Path(__file__).resolve().parent.parent
|
|
20
|
-
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
21
|
-
|
|
22
|
-
from core import BM25, detect_domain, search, search_stack, CSV_CONFIG, AVAILABLE_STACKS
|
|
23
|
-
from design_system import generate_design_system, persist_design_system, DesignSystemGenerator
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class TestTokenizer(unittest.TestCase):
|
|
27
|
-
def test_short_domain_terms_are_kept(self):
|
|
28
|
-
bm25 = BM25()
|
|
29
|
-
tokens = bm25.tokenize("UI and UX design with 3D and AI")
|
|
30
|
-
self.assertIn("ui", tokens)
|
|
31
|
-
self.assertIn("3d", tokens)
|
|
32
|
-
self.assertIn("ai", tokens)
|
|
33
|
-
|
|
34
|
-
def test_stopwords_removed(self):
|
|
35
|
-
bm25 = BM25()
|
|
36
|
-
tokens = bm25.tokenize("this is for the team to do")
|
|
37
|
-
for stopword in ("is", "for", "the", "to", "do"):
|
|
38
|
-
self.assertNotIn(stopword, tokens)
|
|
39
|
-
|
|
40
|
-
def test_synonym_normalization(self):
|
|
41
|
-
bm25 = BM25()
|
|
42
|
-
self.assertEqual(bm25.tokenize("e-commerce store"), bm25.tokenize("ecommerce store"))
|
|
43
|
-
self.assertEqual(bm25.tokenize("dark-mode toggle"), bm25.tokenize("dark toggle"))
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class TestSearchDomains(unittest.TestCase):
|
|
47
|
-
"""Known query -> expected top-domain sanity checks (not exact-row pinning,
|
|
48
|
-
since data can grow; these assert the engine still finds *something*
|
|
49
|
-
relevant for each domain's core vocabulary)."""
|
|
50
|
-
|
|
51
|
-
def test_ui_is_searchable_in_style_domain(self):
|
|
52
|
-
result = search("ui minimalism", domain="style", max_results=1)
|
|
53
|
-
self.assertGreater(result["count"], 0, "literal 'ui' token must be searchable, not filtered by tokenizer")
|
|
54
|
-
|
|
55
|
-
def test_accessibility_query_hits_ux(self):
|
|
56
|
-
result = search("accessibility contrast wcag keyboard", domain="ux", max_results=3)
|
|
57
|
-
self.assertGreater(result["count"], 0)
|
|
58
|
-
|
|
59
|
-
def test_zero_result_query_reports_suggestions_not_error(self):
|
|
60
|
-
result = search("zzqqxx totally made up gibberish", domain="ux", max_results=2)
|
|
61
|
-
self.assertEqual(result["count"], 0)
|
|
62
|
-
self.assertIn("suggestions", result)
|
|
63
|
-
self.assertNotIn("error", result)
|
|
64
|
-
|
|
65
|
-
def test_every_configured_domain_file_exists_and_is_searchable(self):
|
|
66
|
-
for domain, config in CSV_CONFIG.items():
|
|
67
|
-
with self.subTest(domain=domain):
|
|
68
|
-
result = search("design", domain=domain, max_results=1)
|
|
69
|
-
self.assertNotIn("error", result, f"domain '{domain}' failed: {result.get('error')}")
|
|
70
|
-
|
|
71
|
-
def test_every_stack_file_exists_and_is_searchable(self):
|
|
72
|
-
for stack in AVAILABLE_STACKS:
|
|
73
|
-
with self.subTest(stack=stack):
|
|
74
|
-
result = search_stack("performance", stack, max_results=1)
|
|
75
|
-
self.assertNotIn("error", result, f"stack '{stack}' failed: {result.get('error')}")
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
class TestDomainDetection(unittest.TestCase):
|
|
79
|
-
def test_style_keywords_route_to_style(self):
|
|
80
|
-
self.assertEqual(detect_domain("glassmorphism dark ui"), "style")
|
|
81
|
-
|
|
82
|
-
def test_accessibility_keywords_route_to_ux(self):
|
|
83
|
-
self.assertEqual(detect_domain("accessibility contrast wcag"), "ux")
|
|
84
|
-
|
|
85
|
-
def test_ambiguous_query_returns_runner_up(self):
|
|
86
|
-
domain, runner_up = detect_domain("font pairing elegant crypto", return_scores=True)
|
|
87
|
-
self.assertIsNotNone(domain)
|
|
88
|
-
# runner_up may be None if the winning domain has no close second --
|
|
89
|
-
# this just verifies the call shape works without raising.
|
|
90
|
-
|
|
91
|
-
def test_empty_query_falls_back_to_style(self):
|
|
92
|
-
self.assertEqual(detect_domain("...!!!???"), "style")
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
class TestPersistence(unittest.TestCase):
|
|
96
|
-
def test_persist_then_skip_then_force(self):
|
|
97
|
-
with tempfile.TemporaryDirectory() as tmp:
|
|
98
|
-
result = generate_design_system("saas dashboard", "Test Project", persist=True, output_dir=tmp)
|
|
99
|
-
self.assertEqual(result["persistence"]["status"], "success")
|
|
100
|
-
master = Path(result["persistence"]["master_file"])
|
|
101
|
-
self.assertTrue(master.exists())
|
|
102
|
-
original_content = master.read_text(encoding="utf-8")
|
|
103
|
-
|
|
104
|
-
# Second persist without force must not overwrite.
|
|
105
|
-
result2 = generate_design_system("saas dashboard", "Test Project", persist=True, output_dir=tmp)
|
|
106
|
-
self.assertEqual(result2["persistence"]["status"], "skipped_exists")
|
|
107
|
-
self.assertEqual(master.read_text(encoding="utf-8"), original_content)
|
|
108
|
-
|
|
109
|
-
# With force=True it must overwrite.
|
|
110
|
-
result3 = generate_design_system("ecommerce luxury", "Test Project", persist=True, output_dir=tmp, force=True)
|
|
111
|
-
self.assertEqual(result3["persistence"]["status"], "success")
|
|
112
|
-
|
|
113
|
-
def test_persist_writes_only_under_output_dir(self):
|
|
114
|
-
with tempfile.TemporaryDirectory() as tmp:
|
|
115
|
-
generate_design_system("saas dashboard", "Scoped Project", persist=True, output_dir=tmp)
|
|
116
|
-
expected = Path(tmp) / "design-system" / "scoped-project" / "MASTER.md"
|
|
117
|
-
self.assertTrue(expected.exists())
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
class TestReasoningMatch(unittest.TestCase):
|
|
121
|
-
def test_known_category_matches_exactly(self):
|
|
122
|
-
gen = DesignSystemGenerator()
|
|
123
|
-
rule = gen._find_reasoning_rule("SaaS (General)")
|
|
124
|
-
self.assertTrue(rule, "exact-match category lookup should not fall through to fuzzy matching")
|
|
125
|
-
|
|
126
|
-
def test_unknown_category_falls_back_gracefully(self):
|
|
127
|
-
gen = DesignSystemGenerator()
|
|
128
|
-
rule = gen._find_reasoning_rule("Totally Unknown Category XYZ")
|
|
129
|
-
# Should not raise; may return {} which _apply_reasoning handles with defaults.
|
|
130
|
-
self.assertIsInstance(rule, dict)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if __name__ == "__main__":
|
|
134
|
-
unittest.main()
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Regression tests for color-mode coherence in design_system.py (issue #428).
|
|
5
|
-
|
|
6
|
-
Style, palette and anti-patterns used to be resolved independently, so a
|
|
7
|
-
dark-primary style could be returned alongside a light palette and a
|
|
8
|
-
"Dark mode by default" anti-pattern.
|
|
9
|
-
|
|
10
|
-
Stdlib-only (unittest, not pytest) to match test_core.py -- this project ships
|
|
11
|
-
with zero external dependencies.
|
|
12
|
-
|
|
13
|
-
Run with:
|
|
14
|
-
python -m unittest discover -s scripts/tests -v
|
|
15
|
-
or directly:
|
|
16
|
-
python scripts/tests/test_design_system_mode.py
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
import sys
|
|
20
|
-
import unittest
|
|
21
|
-
from pathlib import Path
|
|
22
|
-
|
|
23
|
-
SCRIPTS_DIR = Path(__file__).resolve().parent.parent
|
|
24
|
-
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
25
|
-
|
|
26
|
-
from design_system import ( # noqa: E402
|
|
27
|
-
_filter_anti_patterns_for_mode,
|
|
28
|
-
_palette_is_dark,
|
|
29
|
-
_query_wants_dark,
|
|
30
|
-
_relative_luminance,
|
|
31
|
-
_resolve_color_mode,
|
|
32
|
-
_select_palette_for_mode,
|
|
33
|
-
_style_is_dark_primary,
|
|
34
|
-
DesignSystemGenerator,
|
|
35
|
-
) # noqa: I001 - private helpers first, public class last
|
|
36
|
-
|
|
37
|
-
LIGHT_PALETTE = {"Product Type": "SaaS", "Background": "#F8FAFC", "Foreground": "#020617"}
|
|
38
|
-
DARK_PALETTE = {"Product Type": "Fintech/Crypto", "Background": "#0F172A", "Foreground": "#F8FAFC"}
|
|
39
|
-
|
|
40
|
-
# Verbatim from styles.csv row "Modern Dark (Cinema Mobile)".
|
|
41
|
-
DARK_PRIMARY_STYLE = {
|
|
42
|
-
"Style Category": "Modern Dark (Cinema Mobile)",
|
|
43
|
-
"Light Mode ✓": "✓ Light mode only as exception",
|
|
44
|
-
"Dark Mode ✓": "✓ Dark Mode Primary",
|
|
45
|
-
}
|
|
46
|
-
DUAL_MODE_STYLE = {
|
|
47
|
-
"Style Category": "Minimalism",
|
|
48
|
-
"Light Mode ✓": "✓ Full",
|
|
49
|
-
"Dark Mode ✓": "✓ Full",
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class TestLuminance(unittest.TestCase):
|
|
54
|
-
def test_parses_six_and_three_digit_hex(self):
|
|
55
|
-
self.assertAlmostEqual(_relative_luminance("#FFFFFF"), 1.0, places=6)
|
|
56
|
-
self.assertAlmostEqual(_relative_luminance("#000000"), 0.0, places=6)
|
|
57
|
-
self.assertAlmostEqual(_relative_luminance("#FFF"), 1.0, places=6)
|
|
58
|
-
|
|
59
|
-
def test_returns_none_for_unparseable(self):
|
|
60
|
-
for value in ("", "nope", "#12", "#GGGGGG", None):
|
|
61
|
-
self.assertIsNone(_relative_luminance(value))
|
|
62
|
-
|
|
63
|
-
def test_classifies_backgrounds_from_the_shipped_data(self):
|
|
64
|
-
# Lightest dark background and darkest light background in colors.csv.
|
|
65
|
-
self.assertTrue(_palette_is_dark({"Background": "#1F2937"}))
|
|
66
|
-
self.assertFalse(_palette_is_dark({"Background": "#E8ECF1"}))
|
|
67
|
-
|
|
68
|
-
def test_missing_background_is_not_dark(self):
|
|
69
|
-
self.assertFalse(_palette_is_dark({}))
|
|
70
|
-
self.assertFalse(_palette_is_dark(None))
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
class TestModeResolution(unittest.TestCase):
|
|
74
|
-
def test_dark_primary_style_detected(self):
|
|
75
|
-
self.assertTrue(_style_is_dark_primary(DARK_PRIMARY_STYLE))
|
|
76
|
-
|
|
77
|
-
def test_dual_mode_style_is_not_dark_primary(self):
|
|
78
|
-
self.assertFalse(_style_is_dark_primary(DUAL_MODE_STYLE))
|
|
79
|
-
self.assertFalse(_style_is_dark_primary({}))
|
|
80
|
-
|
|
81
|
-
def test_query_keywords(self):
|
|
82
|
-
self.assertTrue(_query_wants_dark("fintech B2B professional dark mode"))
|
|
83
|
-
self.assertTrue(_query_wants_dark("gaming app OLED"))
|
|
84
|
-
self.assertFalse(_query_wants_dark("healthcare clinic booking app"))
|
|
85
|
-
self.assertFalse(_query_wants_dark(""))
|
|
86
|
-
|
|
87
|
-
def test_either_signal_resolves_dark(self):
|
|
88
|
-
self.assertEqual(_resolve_color_mode("saas dark mode", DUAL_MODE_STYLE), "dark")
|
|
89
|
-
self.assertEqual(_resolve_color_mode("saas", DARK_PRIMARY_STYLE), "dark")
|
|
90
|
-
self.assertEqual(_resolve_color_mode("saas", DUAL_MODE_STYLE), "light")
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
class TestPaletteSelection(unittest.TestCase):
|
|
94
|
-
def test_dark_mode_skips_light_palettes(self):
|
|
95
|
-
chosen = _select_palette_for_mode([LIGHT_PALETTE, DARK_PALETTE], "dark")
|
|
96
|
-
self.assertEqual(chosen["Background"], "#0F172A")
|
|
97
|
-
|
|
98
|
-
def test_dark_mode_falls_back_to_top_hit_when_no_dark_ramp_exists(self):
|
|
99
|
-
chosen = _select_palette_for_mode([LIGHT_PALETTE], "dark")
|
|
100
|
-
self.assertEqual(chosen["Background"], "#F8FAFC")
|
|
101
|
-
|
|
102
|
-
def test_light_mode_keeps_the_existing_top_hit_behaviour(self):
|
|
103
|
-
chosen = _select_palette_for_mode([DARK_PALETTE, LIGHT_PALETTE], "light")
|
|
104
|
-
self.assertEqual(chosen["Background"], "#0F172A")
|
|
105
|
-
|
|
106
|
-
def test_empty_results(self):
|
|
107
|
-
self.assertEqual(_select_palette_for_mode([], "dark"), {})
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
class TestAntiPatternGating(unittest.TestCase):
|
|
111
|
-
def test_dark_clause_dropped_others_kept(self):
|
|
112
|
-
result = _filter_anti_patterns_for_mode(
|
|
113
|
-
"Excessive animation + Dark mode by default", "dark")
|
|
114
|
-
self.assertEqual(result, "Excessive animation")
|
|
115
|
-
|
|
116
|
-
def test_light_mode_is_a_no_op(self):
|
|
117
|
-
original = "Excessive animation + Dark mode by default"
|
|
118
|
-
self.assertEqual(_filter_anti_patterns_for_mode(original, "light"), original)
|
|
119
|
-
|
|
120
|
-
def test_unrelated_anti_patterns_survive_dark_mode(self):
|
|
121
|
-
original = "Complex jargon + Tiny tap targets"
|
|
122
|
-
self.assertEqual(_filter_anti_patterns_for_mode(original, "dark"), original)
|
|
123
|
-
|
|
124
|
-
def test_empty_input(self):
|
|
125
|
-
self.assertEqual(_filter_anti_patterns_for_mode("", "dark"), "")
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
class TestEndToEndCoherence(unittest.TestCase):
|
|
129
|
-
"""The exact reproduction from issue #428."""
|
|
130
|
-
|
|
131
|
-
QUERY = "SaaS invoicing fintech B2B professional dark mode"
|
|
132
|
-
|
|
133
|
-
def test_dark_query_gets_a_dark_background(self):
|
|
134
|
-
ds = DesignSystemGenerator().generate(self.QUERY)
|
|
135
|
-
background = ds["colors"]["background"]
|
|
136
|
-
self.assertTrue(
|
|
137
|
-
_palette_is_dark({"Background": background}),
|
|
138
|
-
"dark-mode query returned a light background: {}".format(background),
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
def test_dark_query_foreground_is_lighter_than_background(self):
|
|
142
|
-
ds = DesignSystemGenerator().generate(self.QUERY)
|
|
143
|
-
background = _relative_luminance(ds["colors"]["background"])
|
|
144
|
-
foreground = _relative_luminance(ds["colors"]["foreground"])
|
|
145
|
-
self.assertIsNotNone(background)
|
|
146
|
-
self.assertIsNotNone(foreground)
|
|
147
|
-
self.assertGreater(foreground, background)
|
|
148
|
-
|
|
149
|
-
def test_dark_query_does_not_advise_against_dark_mode(self):
|
|
150
|
-
ds = DesignSystemGenerator().generate(self.QUERY)
|
|
151
|
-
self.assertNotIn("dark mode", ds["anti_patterns"].lower())
|
|
152
|
-
|
|
153
|
-
def test_light_query_keeps_a_light_background(self):
|
|
154
|
-
ds = DesignSystemGenerator().generate("healthcare clinic booking app")
|
|
155
|
-
self.assertFalse(_palette_is_dark({"Background": ds["colors"]["background"]}))
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if __name__ == "__main__":
|
|
159
|
-
unittest.main(verbosity=2)
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Data integrity guardrail for ui-ux-pro-max. Stdlib-only, no pytest dependency,
|
|
5
|
-
so it can run as a standalone pre-publish/CI check:
|
|
6
|
-
|
|
7
|
-
python validate_data.py
|
|
8
|
-
|
|
9
|
-
Checks, per configured domain/stack CSV:
|
|
10
|
-
- file exists
|
|
11
|
-
- header row contains every column referenced in search_cols/output_cols
|
|
12
|
-
- no duplicate primary-key values (first column) within a file
|
|
13
|
-
- any "Decision_Rules"-style JSON column parses as JSON
|
|
14
|
-
|
|
15
|
-
Exits 0 with no output on success; exits 1 and prints every problem found
|
|
16
|
-
on failure (fail-fast is the wrong call here -- a data change can break
|
|
17
|
-
several files at once, so we want the full list in one run).
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
import csv
|
|
21
|
-
import json
|
|
22
|
-
import sys
|
|
23
|
-
from pathlib import Path
|
|
24
|
-
|
|
25
|
-
from core import CSV_CONFIG, STACK_CONFIG, _STACK_COLS, DATA_DIR
|
|
26
|
-
|
|
27
|
-
# REASONING_FILE lives in design_system.py, not core.py -- redeclared here to
|
|
28
|
-
# avoid a circular import (design_system.py imports core.py).
|
|
29
|
-
REASONING_FILE = "ui-reasoning.csv"
|
|
30
|
-
JSON_COLUMNS = {"Decision_Rules"}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def _read_rows(filepath):
|
|
34
|
-
with open(filepath, "r", encoding="utf-8") as f:
|
|
35
|
-
reader = csv.DictReader(f)
|
|
36
|
-
return reader.fieldnames or [], list(reader)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _check_file(label, filepath, search_cols, output_cols, problems):
|
|
40
|
-
if not filepath.exists():
|
|
41
|
-
problems.append(f"[{label}] missing file: {filepath}")
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
headers, rows = _read_rows(filepath)
|
|
46
|
-
except (csv.Error, UnicodeDecodeError, OSError) as e:
|
|
47
|
-
problems.append(f"[{label}] failed to parse {filepath.name}: {e}")
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
header_set = set(headers)
|
|
51
|
-
for col in set(search_cols) | set(output_cols):
|
|
52
|
-
if col not in header_set:
|
|
53
|
-
problems.append(f"[{label}] {filepath.name}: expected column '{col}' not found in header")
|
|
54
|
-
|
|
55
|
-
# Only check for duplicates against an actual identifier column ("No" is
|
|
56
|
-
# the sequential-index convention used across this dataset). The first
|
|
57
|
-
# CSV column is not reliably a unique key -- e.g. stack files use
|
|
58
|
-
# "Category", which legitimately repeats across many guideline rows.
|
|
59
|
-
if "No" in header_set:
|
|
60
|
-
seen = {}
|
|
61
|
-
for i, row in enumerate(rows, start=2): # +1 header, +1 to be 1-indexed
|
|
62
|
-
key = row.get("No", "")
|
|
63
|
-
if key in seen:
|
|
64
|
-
problems.append(
|
|
65
|
-
f"[{label}] {filepath.name}: duplicate 'No' value '{key}' on rows {seen[key]} and {i}"
|
|
66
|
-
)
|
|
67
|
-
else:
|
|
68
|
-
seen[key] = i
|
|
69
|
-
elif label.startswith("stack:"):
|
|
70
|
-
problems.append(
|
|
71
|
-
f"[{label}] {filepath.name}: missing 'No' index column present in other stack files "
|
|
72
|
-
"(schema drift -- harmless for search, but inconsistent with the rest of data/stacks/)"
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
for row_idx, row in enumerate(rows, start=2):
|
|
76
|
-
for col in JSON_COLUMNS:
|
|
77
|
-
if col in row and row[col]:
|
|
78
|
-
try:
|
|
79
|
-
json.loads(row[col])
|
|
80
|
-
except json.JSONDecodeError as e:
|
|
81
|
-
problems.append(
|
|
82
|
-
f"[{label}] {filepath.name} row {row_idx}: column '{col}' is not valid JSON: {e}"
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def main():
|
|
87
|
-
problems = []
|
|
88
|
-
|
|
89
|
-
for domain, config in CSV_CONFIG.items():
|
|
90
|
-
_check_file(f"domain:{domain}", DATA_DIR / config["file"],
|
|
91
|
-
config["search_cols"], config["output_cols"], problems)
|
|
92
|
-
|
|
93
|
-
for stack, config in STACK_CONFIG.items():
|
|
94
|
-
_check_file(f"stack:{stack}", DATA_DIR / config["file"],
|
|
95
|
-
_STACK_COLS["search_cols"], _STACK_COLS["output_cols"], problems)
|
|
96
|
-
|
|
97
|
-
reasoning_path = DATA_DIR / REASONING_FILE
|
|
98
|
-
if reasoning_path.exists():
|
|
99
|
-
_check_file("reasoning", reasoning_path, ["UI_Category"], ["UI_Category", "Decision_Rules"], problems)
|
|
100
|
-
else:
|
|
101
|
-
problems.append(f"[reasoning] missing file: {reasoning_path}")
|
|
102
|
-
|
|
103
|
-
if problems:
|
|
104
|
-
print(f"FAILED: {len(problems)} data integrity issue(s) found:\n")
|
|
105
|
-
for p in problems:
|
|
106
|
-
print(f" - {p}")
|
|
107
|
-
sys.exit(1)
|
|
108
|
-
|
|
109
|
-
print(f"OK: validated {len(CSV_CONFIG)} domain files, {len(STACK_CONFIG)} stack files, and ui-reasoning.csv")
|
|
110
|
-
sys.exit(0)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if __name__ == "__main__":
|
|
114
|
-
main()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bash-python-guide.d.ts","sourceRoot":"","sources":["../../src/hooks/bash-python-guide.ts"],"names":[],"mappings":"AAmCA,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CA6B5D;AAED,mBAAmB;AACnB,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.pythonGuideHook = pythonGuideHook;
|
|
4
|
-
exports._resetPythonGuideState = _resetPythonGuideState;
|
|
5
|
-
/**
|
|
6
|
-
* bash-python-guide — 温和引导:检测 bash 里的长 Python 内联脚本,提示改用
|
|
7
|
-
* mafw_python(持久内核)。仅"计算型 + 较长"的脚本触发,每会话只提示一次,
|
|
8
|
-
* 避免干扰。
|
|
9
|
-
*/
|
|
10
|
-
const GUIDE_NOTE = '[提示] 这是一段较长的 Python 计算脚本。此类计算建议改用 mafw_python 工具(会话持久内核:变量/导入跨调用保持,' +
|
|
11
|
-
'matplotlib 可直接出图)。bash 适合跑项目脚本/CLI/单行命令。';
|
|
12
|
-
// 会话级去重
|
|
13
|
-
const notifiedSessions = new Set();
|
|
14
|
-
const PY_INLINE_RE = /(?:^|[\n$>])\s*(?:python3?|py)\s+-c\s+['"]/;
|
|
15
|
-
const PY_HEREDOC_RE = /(?:^|[\n$>])\s*(?:python3?|py)\s+[^\n]*<<['"]?(?:EOF|PYTHON|PY)[\s\S]{200,}/;
|
|
16
|
-
function isComputational(code) {
|
|
17
|
-
const keywords = [
|
|
18
|
-
'import numpy', 'import pandas', 'import matplotlib', 'import math', 'import statistics',
|
|
19
|
-
'sum(', 'mean(', 'max(', 'min(', 'sorted(', 'for ', 'lambda ', 'def ', '.mean()', '.sum()',
|
|
20
|
-
'range(', 'percent', 'average', 'median', 'std(', '计算', '统计', '均值',
|
|
21
|
-
];
|
|
22
|
-
return keywords.some((k) => code.includes(k));
|
|
23
|
-
}
|
|
24
|
-
function messageText(msg) {
|
|
25
|
-
const parts = Array.isArray(msg?.parts) ? msg.parts : typeof msg?.content === 'string' ? [{ type: 'text', text: msg.content }] : [];
|
|
26
|
-
return parts
|
|
27
|
-
.filter((p) => (p?.type === 'text' || p?.type === 'tool') && typeof p.text === 'string')
|
|
28
|
-
.map((p) => p.text)
|
|
29
|
-
.join('\n');
|
|
30
|
-
}
|
|
31
|
-
function pythonGuideHook(input, output) {
|
|
32
|
-
const sessionID = input?.sessionID || '';
|
|
33
|
-
if (!sessionID || notifiedSessions.has(sessionID))
|
|
34
|
-
return output;
|
|
35
|
-
const messages = output?.messages;
|
|
36
|
-
if (!Array.isArray(messages))
|
|
37
|
-
return output;
|
|
38
|
-
for (const msg of messages) {
|
|
39
|
-
const isUser = msg?.role === 'user' || msg?.info?.role === 'user';
|
|
40
|
-
const isTool = (msg?.info?.role === 'tool' || msg?.role === 'tool' || msg?.info?.role === 'assistant') && /bash|execute/i.test(String(msg?.info?.name || msg?.name || ''));
|
|
41
|
-
if (!isUser && !isTool)
|
|
42
|
-
continue;
|
|
43
|
-
const text = messageText(msg);
|
|
44
|
-
if (!text)
|
|
45
|
-
continue;
|
|
46
|
-
const matched = PY_INLINE_RE.test(text) || PY_HEREDOC_RE.test(text);
|
|
47
|
-
if (!matched)
|
|
48
|
-
continue;
|
|
49
|
-
// 提取脚本片段做"计算型"判断(截取 python 命令后的内容)
|
|
50
|
-
const afterCmd = text.replace(/.*(?:python3?|py)\s+-c\s+['"]([^'"]{0,3000})['"]?.*/s, '$1');
|
|
51
|
-
const scriptSample = afterCmd.length > 30 ? afterCmd : text;
|
|
52
|
-
if (!isComputational(scriptSample))
|
|
53
|
-
continue;
|
|
54
|
-
notifiedSessions.add(sessionID);
|
|
55
|
-
messages.push({
|
|
56
|
-
role: 'user',
|
|
57
|
-
parts: [{ type: 'text', text: GUIDE_NOTE, synthetic: true }],
|
|
58
|
-
});
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
return output;
|
|
62
|
-
}
|
|
63
|
-
/** 测试辅助:重置会话去重。 */
|
|
64
|
-
function _resetPythonGuideState() {
|
|
65
|
-
notifiedSessions.clear();
|
|
66
|
-
}
|
|
67
|
-
//# sourceMappingURL=bash-python-guide.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bash-python-guide.js","sourceRoot":"","sources":["../../src/hooks/bash-python-guide.ts"],"names":[],"mappings":"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"}
|
package/dist/hooks/handoff.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"handoff.d.ts","sourceRoot":"","sources":["../../src/hooks/handoff.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpE"}
|