Adds a community-driven Store where any authenticated user uploads
skills/agents/plugins as ZIPs, plus /my-ai-stack as the per-user
composition view. The served Claude Code marketplace is now:
(admin_granted ∖ opt_outs) ∪ store_installs
Skill + agent installs are merged into a single `agnes-store-bundle`
plugin in the served marketplace; type=plugin uploads stay standalone.
Names are suffixed with `-by-<owner-username>` at upload time so two
owners can use the same display name without colliding in Claude Code's
flat skill/agent namespace.
Schema v23 → v24 adds three tables:
- store_entities — community-uploaded skills/agents/plugins
- user_store_installs — what each user has chosen to install
- user_plugin_optouts — opt-out overlay on top of admin grants
Admin grant-delete drops every user's opt-out for that plugin so
re-grant resets cleanly to enabled (no sticky personal preference).
UI:
- /store — e-commerce-style listing with type/category/owner
filters, search, pagination, owner-aware [Install]
buttons, clickable cards
- /store/new — 2-step upload wizard with drag & drop, preview
validation (POST /api/store/entities/preview), docs
multi-upload, photo + video URL
- /store/{id} — detail page with hero, file list, docs, owner
actions (Edit/Delete) for the uploader
- /my-ai-stack — Granted plugins (toggle opt-out) + From the Store
(uninstall) sections
- Admin nav: Marketplaces moved into Admin dropdown, renamed to
"Curated Marketplaces"
Validation hardening: type-mismatch guards reject skill ZIP uploaded as
agent (or vice versa), and plugin ZIPs masquerading as skills/agents.
Human-readable error messages mapped client-side from machine codes.
Cross-source naming: Store entity-id-prefixed dirs (`plugins/store-<id>/`)
plus the bundle (`plugins/store-bundle/`) avoid collisions with admin
marketplaces (whose `store` slug is reserved by `is_valid_slug`).
Bundle composition is content-hashed at serve time — install/uninstall
or owner re-upload bumps the bundle's plugin.json `version`, so Claude
Code's auto-update toggle picks up changes.
Tests: 50+ new tests across naming, repositories, filter (admin ∪ store
∪ bundle), API (upload/install/uninstall/delete/preview/docs), end-to-end
marketplace.zip with bundle merging.
28 lines
836 B
Python
28 lines
836 B
Python
"""Predefined Store category taxonomy.
|
|
|
|
The Store organizes uploaded skills / agents / plugins by **subject matter**,
|
|
not by RBAC groups. The categories here are the controlled vocabulary that
|
|
the upload form, listing filters, and `_validate_category` all read from.
|
|
|
|
Adding a category: append a string to ``STORE_CATEGORIES``. Existing entities
|
|
referencing categories that have since been removed continue to surface (they
|
|
just won't match the dropdown filters until re-saved with a current value).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
STORE_CATEGORIES: list[str] = [
|
|
"Code & Engineering",
|
|
"Data & Analytics",
|
|
"Documentation",
|
|
"Productivity",
|
|
"Communication",
|
|
"DevOps & Infra",
|
|
"Security",
|
|
"Research",
|
|
"Other",
|
|
]
|
|
|
|
|
|
def is_valid_category(value: str) -> bool:
|
|
return value in STORE_CATEGORIES
|