Feat/upstream/skills marketplace#1530
Conversation
… config model, exception mapping, and tests - Add SkillMarketSpec, SkillsMarketConfig Pydantic models to config.py with support for ttl_sec, overwrite_default, per-market trust/enable/order - Add /skills/markets (GET/PUT), /skills/markets/validate (POST), /skills/marketplace (GET), /skills/marketplace/install (POST) endpoints - Map _load_market_index() exceptions to structured HTTP errors: json.JSONDecodeError/ValueError → 400, RuntimeError/OSError → 502 - Return 409 SKILL_NAME_CONFLICT when skill already installed (overwrite=False) - Add 12 unit + contract tests covering URL normalisation, path safety, exception mapping, and endpoint response shapes
…18n, and overwrite confirmation - Add API types (SkillsMarketSpec, MarketplaceItem, etc.) and methods (getSkillsMarkets, validateSkillsMarket, getMarketplace, installMarketplaceSkill) - Add marketplace tab to Skills page with skill cards grid and refresh progress bar - Add market management modal (add/edit/delete markets, validate URLs) - Add overwrite confirmation dialog: checks installed skills before install; shows Modal.confirm with danger button; passes overwrite=true on confirm - Replace all hardcoded English toast strings with i18n t() calls - Add 12 new i18n keys under skills.* namespace (en + zh)
…(en + zh) - Add skills_market section to config.en/zh.md with JSON example and field table covering version, cache.ttl_sec, install.overwrite_default, markets[] fields - Add 'Skills Marketplace (Git repositories)' subsection to skills.en/zh.md with 4-step usage walkthrough and notes on config location, auto-scan, cache
- Change market URL example from futuremeng/editor-skills to openclaw/openclaw - Set new market default path to skills - Use skills as fallback display value for empty market path input
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request delivers a significant new feature: a Git-backed Skills Marketplace. It provides a robust system for users to discover, manage, and install skills from various Git repositories directly within the application. The changes span across the backend, console UI, and documentation, ensuring a seamless experience for configuring market sources, validating their integrity, and handling skill installations, including conflict resolution. This enhancement aims to broaden the accessibility and expandability of available skills. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive Skills Marketplace feature, allowing users to browse and install skills from configured Git repositories. The changes are extensive, touching the backend API, frontend UI, configuration, tests, and documentation. The implementation is robust, with new API endpoints for managing markets, a rich console UI for browsing and configuration, and thorough backend logic for aggregation and installation. The addition of unit tests for the new backend logic and updates to documentation are also commendable. I've provided a couple of suggestions on the frontend to improve maintainability and user experience.
| const applyMarketUrlDerivation = (id: string) => { | ||
| if (!autoParseEnabled) { | ||
| return; | ||
| } | ||
| setMarketDraft((prev) => { | ||
| let changed = false; | ||
| let changedBranch = ""; | ||
| let changedPath = ""; | ||
| const nextDraft = prev.map((item) => { | ||
| if (item.id !== id) { | ||
| return item; | ||
| } | ||
|
|
||
| const parsed = parseMarketUrlInput(item.url); | ||
| if (!parsed) { | ||
| return item; | ||
| } | ||
|
|
||
| const nextUrl = parsed.repoUrl; | ||
| const nextBranch = parsed.branch ?? item.branch; | ||
| const nextPath = parsed.path ?? item.path; | ||
| const nextName = item.name.trim() ? item.name : parsed.ownerRepo; | ||
| changed = | ||
| nextUrl !== item.url || | ||
| nextBranch !== item.branch || | ||
| nextPath !== item.path || | ||
| nextName !== item.name; | ||
| changedBranch = nextBranch || ""; | ||
| changedPath = nextPath || ""; | ||
|
|
||
| return { | ||
| ...item, | ||
| url: nextUrl, | ||
| name: nextName, | ||
| branch: nextBranch, | ||
| path: nextPath, | ||
| }; | ||
| }); | ||
|
|
||
| setMarketValidation((current) => { | ||
| const next = { ...current }; | ||
| next[id] = { ok: false, warnings: [] }; | ||
| return next; | ||
| }); | ||
|
|
||
| if (changed) { | ||
| message.success( | ||
| t("skills.marketUrlAutoParsed", { | ||
| branch: changedBranch || "-", | ||
| path: changedPath || "-", | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| return nextDraft; | ||
| }); | ||
| }; |
There was a problem hiding this comment.
The logic inside this function is very similar to the logic inside handleMarketUrlInput (lines 411-471) when autoParseEnabled is true. This duplication can make future maintenance harder as changes would need to be applied in two places.
Consider refactoring this shared logic into a helper function to keep the code DRY (Don't Repeat Yourself) and improve maintainability.
| </div> | ||
| ) : null} | ||
| {marketDraft.map((market) => ( | ||
| <div className={styles.marketManageCard} key={market.id}> |
There was a problem hiding this comment.
Using market.id as the key here can lead to a poor user experience. Since the market.id is editable in the form below, changing it will cause React to see a new component, unmounting the old one and mounting a new one. This will cause the input field for the ID to lose focus while the user is typing in it.
To fix this, you should use a stable identifier for the key that does not change during the lifetime of the component in the list. One approach is to generate a unique, internal ID for each draft item when it's created and use that for the key, while the user-editable id remains a separate property.
There was a problem hiding this comment.
Pull request overview
Adds a Git-backed Skills Marketplace feature to CoPaw, spanning backend APIs/config, Console UI, unit tests, and docs so users can configure multiple market sources, browse aggregated skills, and install skills with conflict/overwrite handling.
Changes:
- Backend: introduce
skills_marketconfig schema and new/skills/markets,/skills/marketplace, and install/validate endpoints with aggregation + caching. - Console: add Marketplace tab, market management modal (add/validate/save), marketplace browsing, and install flow with overwrite confirmation.
- Docs/tests: document marketplace/config fields (EN/ZH) and add unit tests for market parsing, validation, aggregation, and install not-found behavior.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| website/public/docs/skills.zh.md | Adds Chinese docs for the Git skills marketplace UI and behavior. |
| website/public/docs/skills.en.md | Adds English docs for the Git skills marketplace UI and behavior. |
| website/public/docs/config.zh.md | Documents skills_market config schema (ZH) with example. |
| website/public/docs/config.en.md | Documents skills_market config schema (EN) with example. |
| tests/unit/app/routers/test_skills_market.py | Adds backend unit tests covering normalization, validation error mapping, and endpoint shapes. |
| src/copaw/config/config.py | Adds Pydantic models for skills_market and wires it into root Config. |
| src/copaw/app/routers/skills.py | Implements market config endpoints, validation, aggregation/caching, and marketplace install endpoint. |
| console/src/pages/Agent/Skills/useSkills.ts | Adds hook logic for markets CRUD, marketplace fetch/refresh, and install flow. |
| console/src/pages/Agent/Skills/index.tsx | Adds Marketplace UI (tab switch, manage markets modal, search, cards, install/view-source). |
| console/src/pages/Agent/Skills/index.module.less | Styles new marketplace and market management UI components. |
| console/src/locales/zh.json | Adds ZH i18n strings for marketplace/markets UI. |
| console/src/locales/en.json | Adds EN i18n strings for marketplace/markets UI. |
| console/src/api/types/skill.ts | Adds API types for markets, marketplace responses, and install payload/results. |
| console/src/api/modules/skill.ts | Adds API client methods for markets, validate, marketplace fetch, and marketplace install. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| if source_path: | ||
| branch = source_branch or "main" | ||
| source_url = source_url.replace(".git", "") | ||
| if "github.com/" in source_url: | ||
| install_url = ( | ||
| f"{source_url}/tree/{branch}/{source_path.lstrip('/')}" | ||
| ) |
| source_url=source_url, | ||
| install_url=install_url, | ||
| tags=tags if isinstance(tags, list) else [], | ||
| ), |
| meta = { | ||
| "refreshed_at": int(now), | ||
| "cache_hit": False, | ||
| "enabled_market_count": len(enabled_markets), | ||
| "success_market_count": success_count, |
| const base = (item.install_url || item.source_url || "").trim(); | ||
| if (!base) { | ||
| return ""; | ||
| } | ||
| if (base.endsWith("/SKILL.md") || base.endsWith("SKILL.md")) { | ||
| return base; | ||
| } | ||
| return `${base.replace(/\/$/, "")}/SKILL.md`; |
Description
Add a Git-backed Skills Marketplace across backend, console UI, tests, and docs, so users can configure multiple market sources, validate market entries, browse aggregated skills, and install with overwrite confirmation when conflicts exist.
This PR also includes a follow-up UX fix:
Related Issue: Relates to #TBD
Security Considerations: No new auth surface introduced. Market config is handled via existing config flow. Error mapping was tightened to avoid unstructured 500 responses.
Type of Change
Component(s) Affected
Checklist
Testing
Backend
Frontend
Local Verification Evidence
pre-commit run --all-files
Not completed in this session due local pre-commit environment/bootstrap interruption.
pytest tests/unit/ -q
90 passed in local run
npm --prefix console run -s tsc -- --noEmit
pass
Additional Notes
Commits were split atomically (backend, console, docs, follow-up fix), and the same follow-up fix was synchronized on both dual-track branches.