open-ux 0.1.1__tar.gz
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.
- open_ux-0.1.1/.gitignore +56 -0
- open_ux-0.1.1/LICENSE +21 -0
- open_ux-0.1.1/PKG-INFO +159 -0
- open_ux-0.1.1/README.md +137 -0
- open_ux-0.1.1/hatch_build.py +33 -0
- open_ux-0.1.1/hatch_metadata.py +40 -0
- open_ux-0.1.1/pyproject.toml +58 -0
- open_ux-0.1.1/src/open_ux/__init__.py +13 -0
- open_ux-0.1.1/src/open_ux/__main__.py +74 -0
- open_ux-0.1.1/src/open_ux/_version.py +24 -0
- open_ux-0.1.1/src/open_ux/audit.py +136 -0
- open_ux-0.1.1/src/open_ux/auth.py +191 -0
- open_ux-0.1.1/src/open_ux/catalog.py +646 -0
- open_ux-0.1.1/src/open_ux/data/catalog/MANIFEST.md +362 -0
- open_ux-0.1.1/src/open_ux/data/catalog/README.md +77 -0
- open_ux-0.1.1/src/open_ux/data/catalog/index.json +2605 -0
- open_ux-0.1.1/src/open_ux/data/catalog/jobs.json +1012 -0
- open_ux-0.1.1/src/open_ux/data/catalog/manifest.json +1592 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/ant/conversation-vs-navigation-order.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/ant/danger-cancel-primary-when-not-recommended.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/ant/footer-buttons-when-body-cannot-carry.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/ant/group-spacing-overflow-dropdown.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/ant/named-button-type-map.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/ant/never-two-icons-in-one-button.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/ant/ok-cancel-default-vs-explicit-verb.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/ant/one-cta-per-screen.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/ant/prefer-undo-or-popconfirm-over-modal.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/canada/supertask-once-never-promo.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/fluent/apply-versus-save-prefer-autosave.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/fluent/create-labels-new-thing.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/fluent/end-of-task-labels-named.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/fluent/ios-chrome-back-done-cancel.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/fluent/multistep-next-not-continue.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/fluent/split-button-dont-repeat-primary.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/gold/next-primary-save-progress-secondary.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/gold/visual-loudness-primary-secondary-tertiary.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/govuk/button-types-named.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/govuk/dont-use-disabled-buttons.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/govuk/stop-accidental-double-submit.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/mui/button-variant-roles.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/polar/encourage-action-cta-verb.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/suomi/button-max-three-words-mobile-full-width.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/action_panel.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/button_groups.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/cancelability.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/command_history.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/hover_or_popup_tools.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/macros.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/multilevel_undo.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/preview.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/prominent_done_or_assumed_next.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/tidwell/smart_menu_items.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/uswds/filled-next-outline-this-page.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/actions/uswds/real-button-with-type-space-vs-enter.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/equivalent-content-both-official-languages.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/find-understand-use-nonpriority-must-not-block.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/four-writing-qualities.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/intranet-destinations-labelled.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/inverted-pyramid-no-welcome-filler.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/lists-max-7-or-split.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/must-need-may-might.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/no-click-here-same-text-same-destination.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/no-eg-ie-spell-out-and.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/underline-links-only-no-italics-emphasis.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/unique-title-one-h1-heading-every-200-words.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/canada/you-your-never-i-my.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/gold/no-abbr-expand-on-first-mention.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/100pct-stacked-show-absolute-totals.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/chart-anatomy-title-insight-table.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/chart-text-horizontal.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/charts-start-with-story.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/direct-labels-over-legends.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/established-colour-conventions.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/honest-baseline-bars-zero-or-truncation.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/lines-dash-fills-patterns.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/map-pan-from-centre-select-jumps-listing.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/maps-never-only-channel.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/no-tooltip-only-values.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/content/nsw/white-canvas-single-colour-when-labelled.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/copy/polar/dont-duplicate-content.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/copy/polar/guide-dont-prescribe.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/copy/polar/match-shopify-admin-voice.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/copy/polar/plain-language-grade-7.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/dialogs/mui/avoid-apologies-alarm-ambiguity-in-dialog-titles.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/dialogs/mui/confirmation-cancel-discards.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/dialogs/mui/most-alerts-dont-need-titles.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/dialogs/mui/non-modal-dialogs-allowed.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/dialogs/polar/dont-use-modals-for-complex-forms.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/empty_failure/nl/name-next-organisational-steps.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/empty_failure/nl/success-confirms-what-not-bounce-home.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/ant/dialog-feedback-only-important-actionable.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/ant/hover-detail-overlay-timing.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/ant/message-not-for-important-failures.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/bundle-instead-of-spam-toasts.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/dont-place-mobile-toasts-over-navigation.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/one-toast-at-a-time.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/toast-at-most-one-action.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/toast-auto-dismiss-min-5s.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/toast-no-redundant-dismiss.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/toast-placement-geometry.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/toast-sentence-case-generic-tone.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/toast-vs-dialog.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/toast-writing-formulas.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/spectrum/users-can-turn-off-alert-types.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/suomi/notification-first-load-not-post-action.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/suomi/toast-10s-hover-stick-then-float.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/uswds/alert-first-word-is-type.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/uswds/alerts-dont-blame-name-next-step.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/uswds/dont-hide-then-reveal-alerts.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/feedback/uswds/site-alert-dont-stack-dont-panic.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/checkbox-vs-switch.json +36 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/dropdown-when-over-5-no-truncate.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/editable-list-pattern-by-field-count.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/form-in-modal-lifecycle.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/inline-input-feedback-does-not-auto-dismiss.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/input-overlay-click-outside-preserves.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/label-left-default-vs-top.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/no-multi-column-form-in-one-area.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/radio-count-2-to-5.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/settings-mode-and-grouping-thresholds.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/ant/slider-intensity-grade.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/canada/interactive-questions-max-7-same-place.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/fluent/disabled-field-label-stays-perceivable.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/fluent/helper-text-below.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/fluent/labels-top-aligned-by-default.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/fluent/validation-messages-brief-no-end-punctuation.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/gold/avoid-select-except-long-lists.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/gold/search-input-attributes.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/calendar-control-when.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/character-count-fieldset-legend.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/conditionally-reveal-related-question.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/date-input-only-memorable.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/dob-autocomplete-tokens.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/dont-assume-single-vs-multi-from-visuals.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/error-summary-plus-per-field.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/example-dates-unambiguous-order.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/file-upload-reuse-already-uploaded.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/four-date-types.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/item-hints-one-sentence.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/labels-sentence-case-no-colons-above.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/never-auto-tab-date-fields.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/question-pages-continue-back-no-sliders.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/radio-divider-or.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/radios-left-of-labels.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/select-last-resort.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/select-preselect-settings-not-questions.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/govuk/task-list-statuses.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/material/errors.replace_supporting_text.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/mui/dont-type-number.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/mui/explicit-id-for-label-helper.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/mui/helper-text-changes-field-height.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/nl/form-draft-links-new-tab-named-in-link.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/nl/header-search-placeholder-visible-search-control.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/nl/labels-contain-only-text.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/nl/long-legal-jargon-before-form-not-tooltip.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/nl/native-required-bubbles-not-error-ui.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/nl/no-min-max-length-names-places.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/nl/no-select-multiple.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/nl/privacy-terms-link-outside-label.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/nl/step-n-of-m-in-title-and-above-form.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/polar/default-option-selected-when-possible.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/polar/labels-1-3-words-sentence-case-i18n.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/polar/long-form-errors-banner-plus-inline.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/polar/select-4plus-choice-list-under-4.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/polar/validate-after-finished-interacting.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/asterisk-is-icon-not-label-text.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/error-icon-overrides-validation-icon.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/min-width-1-5x-field-height.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/mixed-values-en-dash.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/picker-concise-items-full-value.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/picker-ghost-text-placeholder.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/picker-min-width-2x-height.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/picker-mobile-tray-when-overwhelming.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/picker-no-max-menu-height.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/picker-readonly-only-if-selection.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/quiet-vs-standard-background.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/readonly-vs-disabled.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/tab-into-field-selects-text.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/spectrum/validation-icon-when-format-valid.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/suomi/date-hint-ppkkvvvv-calendar-optional-focus-trap.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/suomi/errorsummary-link-text-equals-field-error.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/suomi/file-hint-and-datetime-fieldset.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/suomi/radio-extras-hidden-then-indented.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/suomi/search-hide-label-only-if-name-states-context.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/suomi/static-validation-on-submit-default.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/suomi/time-field-60px-hint-tmm-always-visible.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/tidwell/inputs.autocompletion.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/tidwell/inputs.dropdown_chooser.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/tidwell/inputs.fill_in_the_blanks.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/tidwell/inputs.good_defaults_smart_prefills.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/tidwell/inputs.list_builder.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/tidwell/inputs.password_strength_meter.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/tidwell/inputs.structured_format.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/uswds/character-count-brevity-or-legal.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/uswds/date-picker-when-weekday-always-type.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/uswds/dont-split-phone-ssn-card.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/uswds/forms-simple-vertical-stack.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/vercel/errors.focus_first_error.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/vercel/inputs.html_autocomplete_and_name.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/vercel/inputs.shared_hit_target_choice.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/vercel/labels.clickable.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/vercel/navigation.warn_unsaved.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/forms/vercel/submit.enabled_until_request.json +31 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/foundations/gold/consistent-not-uniform.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/hit_targets/gold/card-one-inner-link-hit-area.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/system_status/nl/dont-auto-dismiss-status-announce-complete.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/trust/nsw/privacy-by-design-pia-security-day-one.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/canada/doormats-max-9.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/canada/no-separate-mobile-site.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/gold/accordion-find-in-page-misses-collapsed.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/gold/progress-indicator-out-of-order-returnable.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/gold/search-placement-home-vs-results.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/suomi/hide-breadcrumbs-on-mobile.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/suomi/pagination-skip-link-to-listing.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/uswds/banner-gov-mil-https-every-page.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/uswds/homepage-search-box-not-link.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/uswds/identifier-darker-than-footer.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/rules/wayfinding/uswds/identifier-required-links-not-footer-dup.json +32 -0
- open_ux-0.1.1/src/open_ux/data/catalog/schema.json +218 -0
- open_ux-0.1.1/src/open_ux/health.py +20 -0
- open_ux-0.1.1/src/open_ux/http_app.py +39 -0
- open_ux-0.1.1/src/open_ux/jobs.py +484 -0
- open_ux-0.1.1/src/open_ux/public_html.py +344 -0
- open_ux-0.1.1/src/open_ux/rate_limit.py +54 -0
- open_ux-0.1.1/src/open_ux/server.py +607 -0
- open_ux-0.1.1/src/open_ux/settings.py +93 -0
- open_ux-0.1.1/src/open_ux/situations.py +295 -0
- open_ux-0.1.1/src/open_ux/static/favicon.svg +3 -0
- open_ux-0.1.1/src/open_ux/static/logo-mark.svg +7 -0
- open_ux-0.1.1/src/open_ux/store.py +341 -0
- open_ux-0.1.1/tests/conftest.py +45 -0
- open_ux-0.1.1/tests/fixtures/situation_queries.json +68 -0
- open_ux-0.1.1/tests/test_agent_path.py +304 -0
- open_ux-0.1.1/tests/test_agent_scripts.py +140 -0
- open_ux-0.1.1/tests/test_audit.py +207 -0
- open_ux-0.1.1/tests/test_auth.py +66 -0
- open_ux-0.1.1/tests/test_branding.py +62 -0
- open_ux-0.1.1/tests/test_catalog.py +583 -0
- open_ux-0.1.1/tests/test_consent.py +51 -0
- open_ux-0.1.1/tests/test_health.py +32 -0
- open_ux-0.1.1/tests/test_invite.py +352 -0
- open_ux-0.1.1/tests/test_privacy.py +83 -0
- open_ux-0.1.1/tests/test_public_html.py +60 -0
- open_ux-0.1.1/tests/test_rate_limit.py +58 -0
- open_ux-0.1.1/tests/test_settings.py +47 -0
- open_ux-0.1.1/tests/test_situations.py +240 -0
- open_ux-0.1.1/tests/test_situations_fixture.py +87 -0
- open_ux-0.1.1/tests/test_tools.py +340 -0
- open_ux-0.1.1/tests/test_uns50_smoke.py +64 -0
- open_ux-0.1.1/tests/test_web.py +164 -0
open_ux-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Generated by hatch-vcs
|
|
2
|
+
packages/mcp/src/open_ux/_version.py
|
|
3
|
+
|
|
4
|
+
# Python
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
*.egg-info/
|
|
9
|
+
.eggs/
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
|
|
20
|
+
# Node
|
|
21
|
+
node_modules/
|
|
22
|
+
npm-debug.log*
|
|
23
|
+
yarn-debug.log*
|
|
24
|
+
yarn-error.log*
|
|
25
|
+
pnpm-debug.log*
|
|
26
|
+
|
|
27
|
+
# Env / secrets
|
|
28
|
+
.env
|
|
29
|
+
.env.*
|
|
30
|
+
!.env.example
|
|
31
|
+
*.pem
|
|
32
|
+
id_rsa
|
|
33
|
+
id_ed25519
|
|
34
|
+
.fly/
|
|
35
|
+
|
|
36
|
+
# Hosted ops live in the private repo. Copy locally to deploy; do not commit.
|
|
37
|
+
/fly.toml
|
|
38
|
+
/railway.toml
|
|
39
|
+
|
|
40
|
+
# Local hosted store
|
|
41
|
+
data/
|
|
42
|
+
*.sqlite
|
|
43
|
+
*.sqlite-journal
|
|
44
|
+
|
|
45
|
+
# Editor / agent local (never publish chats or transcripts)
|
|
46
|
+
.cursor/
|
|
47
|
+
.claude/
|
|
48
|
+
.codex/
|
|
49
|
+
agent-transcripts/
|
|
50
|
+
*.jsonl
|
|
51
|
+
|
|
52
|
+
# OS / editors
|
|
53
|
+
.DS_Store
|
|
54
|
+
*.swp
|
|
55
|
+
.idea/
|
|
56
|
+
.vscode/
|
open_ux-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yonatan Lev Ari
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
open_ux-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: open-ux
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Cited UX rules agents audit against — shared cited guidelines
|
|
5
|
+
Project-URL: Homepage, https://open-ux.dev
|
|
6
|
+
Project-URL: Repository, https://github.com/3dyonic/open-ux
|
|
7
|
+
Project-URL: Issues, https://github.com/3dyonic/open-ux/issues
|
|
8
|
+
Author: 3dyonic
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Requires-Dist: fastmcp<5,>=2.13
|
|
13
|
+
Requires-Dist: jsonschema<5,>=4.23
|
|
14
|
+
Requires-Dist: pydantic>=2
|
|
15
|
+
Requires-Dist: starlette>=0.40
|
|
16
|
+
Requires-Dist: uvicorn>=0.30
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Open UX
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
**Cited UX rules agents can list, fetch, and audit against.**
|
|
28
|
+
|
|
29
|
+
Stop inventing UX guidance from memory. Open UX is a shared, open-source catalog of UX rules with citations, plus tools so an agent can find the right criteria and apply them to work it already has.
|
|
30
|
+
|
|
31
|
+
**Hosted:** [open-ux.dev](https://open-ux.dev) · **License:** [MIT](https://github.com/3dyonic/open-ux/blob/master/LICENSE)
|
|
32
|
+
|
|
33
|
+
## What it is
|
|
34
|
+
|
|
35
|
+
A curated, machine-readable store of UX guidelines and a small tool surface so an agent can:
|
|
36
|
+
|
|
37
|
+
1. **Browse situations** — pick a compose job (Situation Card) that matches the work
|
|
38
|
+
2. **Fetch criteria** — get cited rules for that job (`rule`, `pass_when`, `fail_when`)
|
|
39
|
+
3. **Apply locally** — the client judges the artifact; the host never takes the file and never returns pass/fail
|
|
40
|
+
|
|
41
|
+
There is no server-side LLM. One shared catalog for every caller — an account unlocks the hosted API; it does not give you a private rulebook.
|
|
42
|
+
|
|
43
|
+
## What it is not
|
|
44
|
+
|
|
45
|
+
* A generative design copilot or “does this look good?” scorer
|
|
46
|
+
* A WCAG / accessibility compliance checker (we do not claim conformance, contrast audits, or screen-reader naming)
|
|
47
|
+
* A closed corpus — the catalog and server are open source; you can self-host the same tools
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
* **Cited catalog** — one JSON file per rule, with sources you can follow
|
|
52
|
+
* **Public catalog site** — browse rules in the browser at [`/catalog`](https://open-ux.dev/catalog)
|
|
53
|
+
* **Agent tools** — list / search / get guidelines; suggest situations; audit by need (job or ids)
|
|
54
|
+
* **Hosted or self-host** — waitlist + API key on the hosted service, or stdio locally with no auth
|
|
55
|
+
* **Privacy-minded hosted mode** — we do not store UI payloads or prompts; see [Privacy](https://open-ux.dev/privacy)
|
|
56
|
+
|
|
57
|
+
## Quick start
|
|
58
|
+
|
|
59
|
+
### Hosted
|
|
60
|
+
|
|
61
|
+
1. Request access at [open-ux.dev/invite](https://open-ux.dev/invite)
|
|
62
|
+
2. After approval, redeem your invite for a bearer API key (`uxmcp_…`)
|
|
63
|
+
3. Point your MCP client at the hosted `/mcp` endpoint with that key
|
|
64
|
+
4. Call `list_guidelines` or `audit` with a job (no file upload)
|
|
65
|
+
|
|
66
|
+
Tools return **401** without a key.
|
|
67
|
+
|
|
68
|
+
### Self-host
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install open-ux
|
|
72
|
+
python -m open_ux validate-catalog
|
|
73
|
+
python -m open_ux stdio
|
|
74
|
+
OPEN_UX_MODE=hosted python -m open_ux http
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Browse the local site at `http://127.0.0.1:8080/catalog`. Point MCP clients at local stdio, or at hosted `/mcp` with a `uxmcp_` key.
|
|
78
|
+
|
|
79
|
+
### Contribute from this repo
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
python -m venv .venv && source .venv/bin/activate
|
|
83
|
+
pip install -e "packages/mcp[dev]"
|
|
84
|
+
python -m open_ux validate-catalog
|
|
85
|
+
python -m open_ux stdio
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Tests
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
cd packages/mcp && python -m pytest
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Claude plugin
|
|
95
|
+
|
|
96
|
+
Thin client in [`clients/claude`](https://github.com/3dyonic/open-ux/tree/master/clients/claude). It connects to the catalog; it does not ship a second copy of the rules.
|
|
97
|
+
|
|
98
|
+
## Agent tools
|
|
99
|
+
|
|
100
|
+
| Tool | Purpose |
|
|
101
|
+
| -- | -- |
|
|
102
|
+
| `list_situations` | Page Situation Cards (optional container filter) |
|
|
103
|
+
| `get_situation` | One Card plus facets / rule pointers |
|
|
104
|
+
| `suggest_situations` | Rank Cards from task text (allowlisted) |
|
|
105
|
+
| `list_guidelines` | Paged catalog index |
|
|
106
|
+
| `search_guidelines` | Filter index by query / jobs / lane |
|
|
107
|
+
| `get_guideline` | Full rule body by id |
|
|
108
|
+
| `audit` | Say the need (`jobs` Card/container or `guideline_ids`); get matching criteria |
|
|
109
|
+
|
|
110
|
+
`audit` accepts optional `query` and `limit` (default 10, max 50). It does **not** take a file target and does **not** return a host verdict. If nothing matches, you get an empty list and a note.
|
|
111
|
+
|
|
112
|
+
## Catalog layout
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
catalog/
|
|
116
|
+
rules/{category}/{source}/ one JSON file per rule
|
|
117
|
+
index.json generated index
|
|
118
|
+
jobs.json Situation tree
|
|
119
|
+
schema.json rule schema
|
|
120
|
+
MANIFEST.md human map (no rule bodies)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Rules are never forked per tenant. Soft size budget ~50–100 KB; hard ceiling ~384 KB. Details: [`catalog/README.md`](https://github.com/3dyonic/open-ux/blob/master/catalog/README.md).
|
|
124
|
+
|
|
125
|
+
## Repository layout
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
packages/mcp Python server (FastMCP)
|
|
129
|
+
catalog/ shared rules + schema
|
|
130
|
+
clients/claude thin Claude plugin
|
|
131
|
+
docs/ privacy, assets
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Python package: `open-ux` · npm / plugin scope: `@3dyonic/open-ux`
|
|
135
|
+
|
|
136
|
+
## Hosted vs self-host
|
|
137
|
+
|
|
138
|
+
| | Hosted HTTP | Self-host (stdio) |
|
|
139
|
+
| -- | -- | -- |
|
|
140
|
+
| Auth | Waitlist → invite → bearer `uxmcp_` | None |
|
|
141
|
+
| Rate limits | Per-key and per-IP on `/mcp` | None |
|
|
142
|
+
| Telemetry | Aggregated usage (key hash, tools, rule ids) | Off |
|
|
143
|
+
|
|
144
|
+
Privacy on the hosted product: [open-ux.dev/privacy](https://open-ux.dev/privacy) (Eng constraints also in [`docs/PRIVACY.md`](https://github.com/3dyonic/open-ux/blob/master/docs/PRIVACY.md)).
|
|
145
|
+
|
|
146
|
+
## Contributing
|
|
147
|
+
|
|
148
|
+
Issues and pull requests are welcome. Keep the catalog cited — every rule should point at a real source. Prefer small, reviewable PRs: one concern per change (catalog rows, server behavior, or docs).
|
|
149
|
+
|
|
150
|
+
Before opening a PR:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
python -m open_ux validate-catalog
|
|
154
|
+
cd packages/mcp && python -m pytest
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
[MIT](https://github.com/3dyonic/open-ux/blob/master/LICENSE)
|
open_ux-0.1.1/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Open UX
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
**Cited UX rules agents can list, fetch, and audit against.**
|
|
6
|
+
|
|
7
|
+
Stop inventing UX guidance from memory. Open UX is a shared, open-source catalog of UX rules with citations, plus tools so an agent can find the right criteria and apply them to work it already has.
|
|
8
|
+
|
|
9
|
+
**Hosted:** [open-ux.dev](https://open-ux.dev) · **License:** [MIT](LICENSE)
|
|
10
|
+
|
|
11
|
+
## What it is
|
|
12
|
+
|
|
13
|
+
A curated, machine-readable store of UX guidelines and a small tool surface so an agent can:
|
|
14
|
+
|
|
15
|
+
1. **Browse situations** — pick a compose job (Situation Card) that matches the work
|
|
16
|
+
2. **Fetch criteria** — get cited rules for that job (`rule`, `pass_when`, `fail_when`)
|
|
17
|
+
3. **Apply locally** — the client judges the artifact; the host never takes the file and never returns pass/fail
|
|
18
|
+
|
|
19
|
+
There is no server-side LLM. One shared catalog for every caller — an account unlocks the hosted API; it does not give you a private rulebook.
|
|
20
|
+
|
|
21
|
+
## What it is not
|
|
22
|
+
|
|
23
|
+
* A generative design copilot or “does this look good?” scorer
|
|
24
|
+
* A WCAG / accessibility compliance checker (we do not claim conformance, contrast audits, or screen-reader naming)
|
|
25
|
+
* A closed corpus — the catalog and server are open source; you can self-host the same tools
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
* **Cited catalog** — one JSON file per rule, with sources you can follow
|
|
30
|
+
* **Public catalog site** — browse rules in the browser at [`/catalog`](https://open-ux.dev/catalog)
|
|
31
|
+
* **Agent tools** — list / search / get guidelines; suggest situations; audit by need (job or ids)
|
|
32
|
+
* **Hosted or self-host** — waitlist + API key on the hosted service, or stdio locally with no auth
|
|
33
|
+
* **Privacy-minded hosted mode** — we do not store UI payloads or prompts; see [Privacy](https://open-ux.dev/privacy)
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
### Hosted
|
|
38
|
+
|
|
39
|
+
1. Request access at [open-ux.dev/invite](https://open-ux.dev/invite)
|
|
40
|
+
2. After approval, redeem your invite for a bearer API key (`uxmcp_…`)
|
|
41
|
+
3. Point your MCP client at the hosted `/mcp` endpoint with that key
|
|
42
|
+
4. Call `list_guidelines` or `audit` with a job (no file upload)
|
|
43
|
+
|
|
44
|
+
Tools return **401** without a key.
|
|
45
|
+
|
|
46
|
+
### Self-host
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install open-ux
|
|
50
|
+
python -m open_ux validate-catalog
|
|
51
|
+
python -m open_ux stdio
|
|
52
|
+
OPEN_UX_MODE=hosted python -m open_ux http
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Browse the local site at `http://127.0.0.1:8080/catalog`. Point MCP clients at local stdio, or at hosted `/mcp` with a `uxmcp_` key.
|
|
56
|
+
|
|
57
|
+
### Contribute from this repo
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
python -m venv .venv && source .venv/bin/activate
|
|
61
|
+
pip install -e "packages/mcp[dev]"
|
|
62
|
+
python -m open_ux validate-catalog
|
|
63
|
+
python -m open_ux stdio
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Tests
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cd packages/mcp && python -m pytest
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Claude plugin
|
|
73
|
+
|
|
74
|
+
Thin client in [`clients/claude`](clients/claude). It connects to the catalog; it does not ship a second copy of the rules.
|
|
75
|
+
|
|
76
|
+
## Agent tools
|
|
77
|
+
|
|
78
|
+
| Tool | Purpose |
|
|
79
|
+
| -- | -- |
|
|
80
|
+
| `list_situations` | Page Situation Cards (optional container filter) |
|
|
81
|
+
| `get_situation` | One Card plus facets / rule pointers |
|
|
82
|
+
| `suggest_situations` | Rank Cards from task text (allowlisted) |
|
|
83
|
+
| `list_guidelines` | Paged catalog index |
|
|
84
|
+
| `search_guidelines` | Filter index by query / jobs / lane |
|
|
85
|
+
| `get_guideline` | Full rule body by id |
|
|
86
|
+
| `audit` | Say the need (`jobs` Card/container or `guideline_ids`); get matching criteria |
|
|
87
|
+
|
|
88
|
+
`audit` accepts optional `query` and `limit` (default 10, max 50). It does **not** take a file target and does **not** return a host verdict. If nothing matches, you get an empty list and a note.
|
|
89
|
+
|
|
90
|
+
## Catalog layout
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
catalog/
|
|
94
|
+
rules/{category}/{source}/ one JSON file per rule
|
|
95
|
+
index.json generated index
|
|
96
|
+
jobs.json Situation tree
|
|
97
|
+
schema.json rule schema
|
|
98
|
+
MANIFEST.md human map (no rule bodies)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Rules are never forked per tenant. Soft size budget ~50–100 KB; hard ceiling ~384 KB. Details: [`catalog/README.md`](catalog/README.md).
|
|
102
|
+
|
|
103
|
+
## Repository layout
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
packages/mcp Python server (FastMCP)
|
|
107
|
+
catalog/ shared rules + schema
|
|
108
|
+
clients/claude thin Claude plugin
|
|
109
|
+
docs/ privacy, assets
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Python package: `open-ux` · npm / plugin scope: `@3dyonic/open-ux`
|
|
113
|
+
|
|
114
|
+
## Hosted vs self-host
|
|
115
|
+
|
|
116
|
+
| | Hosted HTTP | Self-host (stdio) |
|
|
117
|
+
| -- | -- | -- |
|
|
118
|
+
| Auth | Waitlist → invite → bearer `uxmcp_` | None |
|
|
119
|
+
| Rate limits | Per-key and per-IP on `/mcp` | None |
|
|
120
|
+
| Telemetry | Aggregated usage (key hash, tools, rule ids) | Off |
|
|
121
|
+
|
|
122
|
+
Privacy on the hosted product: [open-ux.dev/privacy](https://open-ux.dev/privacy) (Eng constraints also in [`docs/PRIVACY.md`](docs/PRIVACY.md)).
|
|
123
|
+
|
|
124
|
+
## Contributing
|
|
125
|
+
|
|
126
|
+
Issues and pull requests are welcome. Keep the catalog cited — every rule should point at a real source. Prefer small, reviewable PRs: one concern per change (catalog rows, server behavior, or docs).
|
|
127
|
+
|
|
128
|
+
Before opening a PR:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
python -m open_ux validate-catalog
|
|
132
|
+
cd packages/mcp && python -m pytest
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CustomBuildHook(BuildHookInterface):
|
|
9
|
+
"""Ship repo catalog/ in the wheel and sdist (not copied into git src/)."""
|
|
10
|
+
|
|
11
|
+
def initialize(self, version: str, build_data: dict) -> None:
|
|
12
|
+
in_tree = Path(self.root) / "src" / "open_ux" / "data" / "catalog"
|
|
13
|
+
repo = Path(self.root).resolve().parent.parent / "catalog"
|
|
14
|
+
if (in_tree / "schema.json").is_file():
|
|
15
|
+
catalog = in_tree
|
|
16
|
+
elif (repo / "schema.json").is_file():
|
|
17
|
+
catalog = repo
|
|
18
|
+
else:
|
|
19
|
+
raise RuntimeError(
|
|
20
|
+
"catalog/schema.json not found; expected repo catalog/ or "
|
|
21
|
+
"sdist src/open_ux/data/catalog"
|
|
22
|
+
)
|
|
23
|
+
dest = (
|
|
24
|
+
"src/open_ux/data/catalog"
|
|
25
|
+
if self.target_name == "sdist"
|
|
26
|
+
else "open_ux/data/catalog"
|
|
27
|
+
)
|
|
28
|
+
build_data.setdefault("force_include", {})
|
|
29
|
+
build_data["force_include"][str(catalog)] = dest
|
|
30
|
+
if self.target_name == "sdist":
|
|
31
|
+
repo_readme = Path(self.root).resolve().parent.parent / "README.md"
|
|
32
|
+
if repo_readme.is_file():
|
|
33
|
+
build_data["force_include"][str(repo_readme)] = "README.md"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from hatchling.metadata.plugin.interface import MetadataHookInterface
|
|
6
|
+
|
|
7
|
+
_REPO = "https://github.com/3dyonic/open-ux"
|
|
8
|
+
_RAW = "https://raw.githubusercontent.com/3dyonic/open-ux/master"
|
|
9
|
+
|
|
10
|
+
_PYPI_LINKS = (
|
|
11
|
+
("](docs/readme-hero.svg)", f"]({_RAW}/docs/readme-hero.svg)"),
|
|
12
|
+
("](LICENSE)", f"]({_REPO}/blob/master/LICENSE)"),
|
|
13
|
+
("](catalog/README.md)", f"]({_REPO}/blob/master/catalog/README.md)"),
|
|
14
|
+
("](clients/claude)", f"]({_REPO}/tree/master/clients/claude)"),
|
|
15
|
+
("](docs/PRIVACY.md)", f"]({_REPO}/blob/master/docs/PRIVACY.md)"),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _for_pypi(text: str) -> str:
|
|
20
|
+
for src, dest in _PYPI_LINKS:
|
|
21
|
+
text = text.replace(src, dest)
|
|
22
|
+
return text
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CustomMetadataHook(MetadataHookInterface):
|
|
26
|
+
"""Use the public root README as the package description."""
|
|
27
|
+
|
|
28
|
+
def update(self, metadata: dict) -> None:
|
|
29
|
+
repo_readme = Path(self.root).resolve().parent.parent / "README.md"
|
|
30
|
+
local_readme = Path(self.root) / "README.md"
|
|
31
|
+
if repo_readme.is_file() and repo_readme.resolve() != local_readme.resolve():
|
|
32
|
+
readme = repo_readme
|
|
33
|
+
elif local_readme.is_file():
|
|
34
|
+
readme = local_readme
|
|
35
|
+
else:
|
|
36
|
+
raise RuntimeError("README.md not found for package metadata")
|
|
37
|
+
metadata["readme"] = {
|
|
38
|
+
"content-type": "text/markdown",
|
|
39
|
+
"text": _for_pypi(readme.read_text(encoding="utf-8")),
|
|
40
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "open-ux"
|
|
7
|
+
dynamic = ["version", "readme"]
|
|
8
|
+
description = "Cited UX rules agents audit against — shared cited guidelines"
|
|
9
|
+
license = "MIT"
|
|
10
|
+
license-files = ["LICENSE"]
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [{ name = "3dyonic" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"fastmcp>=2.13,<5",
|
|
15
|
+
"jsonschema>=4.23,<5",
|
|
16
|
+
"starlette>=0.40",
|
|
17
|
+
"uvicorn>=0.30",
|
|
18
|
+
"pydantic>=2",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.optional-dependencies]
|
|
22
|
+
dev = ["pytest>=8", "pytest-asyncio>=0.24", "httpx>=0.27"]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://open-ux.dev"
|
|
26
|
+
Repository = "https://github.com/3dyonic/open-ux"
|
|
27
|
+
Issues = "https://github.com/3dyonic/open-ux/issues"
|
|
28
|
+
|
|
29
|
+
[project.scripts]
|
|
30
|
+
open-ux = "open_ux.__main__:main"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.version]
|
|
33
|
+
source = "vcs"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.version.raw-options]
|
|
36
|
+
search_parent_directories = true
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.hooks.vcs]
|
|
39
|
+
version-file = "src/open_ux/_version.py"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.metadata.hooks.custom]
|
|
42
|
+
path = "hatch_metadata.py"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.hooks.custom]
|
|
45
|
+
path = "hatch_build.py"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["src/open_ux"]
|
|
49
|
+
|
|
50
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
51
|
+
"src/open_ux/static/favicon.svg" = "open_ux/static/favicon.svg"
|
|
52
|
+
"src/open_ux/static/logo-mark.svg" = "open_ux/static/logo-mark.svg"
|
|
53
|
+
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
pythonpath = ["src"]
|
|
57
|
+
asyncio_mode = "auto"
|
|
58
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Open UX — cited UX rules agents audit against."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from open_ux._version import __version__
|
|
7
|
+
except ImportError: # pragma: no cover - editable tree before first build
|
|
8
|
+
try:
|
|
9
|
+
from importlib.metadata import version
|
|
10
|
+
|
|
11
|
+
__version__ = version("open-ux")
|
|
12
|
+
except Exception:
|
|
13
|
+
__version__ = "0.0.0"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main(argv: list[str] | None = None) -> int:
|
|
10
|
+
parser = argparse.ArgumentParser(prog="open-ux", description="Open UX server")
|
|
11
|
+
parser.add_argument(
|
|
12
|
+
"mode",
|
|
13
|
+
nargs="?",
|
|
14
|
+
choices=("stdio", "http", "validate-catalog", "approve-invite"),
|
|
15
|
+
default=os.environ.get("OPEN_UX_MODE", "stdio"),
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument("email", nargs="?", default="")
|
|
18
|
+
parser.add_argument("--host", default=os.environ.get("HOST", "0.0.0.0"))
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"--port",
|
|
21
|
+
type=int,
|
|
22
|
+
default=int(os.environ.get("PORT", os.environ.get("OPEN_UX_PORT", "8080"))),
|
|
23
|
+
)
|
|
24
|
+
args = parser.parse_args(argv)
|
|
25
|
+
|
|
26
|
+
if args.mode == "validate-catalog":
|
|
27
|
+
from open_ux.catalog import load_catalog
|
|
28
|
+
|
|
29
|
+
catalog = load_catalog()
|
|
30
|
+
print(
|
|
31
|
+
f"catalog ok version={catalog.version} "
|
|
32
|
+
f"guidelines={len(catalog.guidelines)} bytes={catalog.size_bytes}"
|
|
33
|
+
)
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
if args.mode == "approve-invite":
|
|
37
|
+
if not args.email:
|
|
38
|
+
print("usage: open-ux approve-invite EMAIL", file=sys.stderr)
|
|
39
|
+
return 2
|
|
40
|
+
from open_ux.auth import AuthError, approve_invite
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
issued = approve_invite(args.email)
|
|
44
|
+
except AuthError as exc:
|
|
45
|
+
print(str(exc), file=sys.stderr)
|
|
46
|
+
return 1
|
|
47
|
+
print(
|
|
48
|
+
json.dumps(
|
|
49
|
+
{
|
|
50
|
+
"email": issued.email,
|
|
51
|
+
"token": issued.token,
|
|
52
|
+
"token_prefix": "inv_",
|
|
53
|
+
"redeem_url": issued.redeem_url,
|
|
54
|
+
"expires_at": issued.expires_at,
|
|
55
|
+
},
|
|
56
|
+
indent=2,
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
return 0
|
|
60
|
+
|
|
61
|
+
if args.mode == "stdio":
|
|
62
|
+
from open_ux.server import create_mcp
|
|
63
|
+
|
|
64
|
+
create_mcp(hosted=False).run(transport="stdio")
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
from open_ux.http_app import serve_http
|
|
68
|
+
|
|
69
|
+
serve_http(host=args.host, port=args.port)
|
|
70
|
+
return 0
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
sys.exit(main())
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 1)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|