feat: initialize Claude Code marketplace with test plugins and GitHub Pages - #1
feat: initialize Claude Code marketplace with test plugins and GitHub Pages#1fx wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR establishes a Claude Code Marketplace repository with initial test plugins and a web interface. It provides infrastructure for distributing Claude Code agents, skills, and commands through a centralized marketplace that can be installed via chezmoi or manually.
- Creates marketplace infrastructure with a JSON manifest and GitHub Pages deployment
- Adds test plugins (test-skill and test-agent) to verify marketplace functionality
- Provides installation instructions via chezmoi and manual methods
Reviewed Changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| skills/test-skill.md | Adds a test skill with metadata and instructions for verifying marketplace functionality |
| marketplace.json | Creates marketplace manifest listing available plugins with metadata |
| index.html | Implements web interface for browsing marketplace plugins with dynamic loading |
| agents/test-agent.md | Adds a test agent for verifying marketplace integration |
| README.md | Provides documentation for installation, structure, and contribution guidelines |
| LICENSE | Adds MIT license for the marketplace |
| .gitignore | Configures ignored files for macOS, IDE, and build artifacts |
| .github/workflows/pages.yml | Sets up GitHub Actions workflow for deploying to GitHub Pages |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const tags = plugin.tags.map(tag => | ||
| '<span class="tag">' + tag + '</span>' | ||
| ).join(''); | ||
| card.innerHTML = '<div class="plugin-type">' + plugin.type + '</div>' + | ||
| '<div class="plugin-name">' + plugin.name + '</div>' + | ||
| '<div class="plugin-description">' + plugin.description + '</div>' + | ||
| '<div class="tags">' + tags + '</div>' + | ||
| '<div class="plugin-meta">' + | ||
| '<span>v' + plugin.version + '</span>' + | ||
| '<span>by ' + plugin.author + '</span>' + | ||
| '</div>'; | ||
There was a problem hiding this comment.
User-supplied data from marketplace.json is being inserted into the DOM without sanitization. If plugin.name, plugin.description, plugin.type, plugin.version, or plugin.author contain HTML/JavaScript, this creates an XSS vulnerability. Use textContent instead of innerHTML or sanitize the input before insertion.
| const tags = plugin.tags.map(tag => | |
| '<spanclass="tag">' + tag + '</span>' | |
| ).join(''); | |
| card.innerHTML = '<divclass="plugin-type">' + plugin.type + '</div>' + | |
| '<divclass="plugin-name">' + plugin.name + '</div>' + | |
| '<divclass="plugin-description">' + plugin.description + '</div>' + | |
| '<divclass="tags">' + tags + '</div>' + | |
| '<divclass="plugin-meta">' + | |
| '<span>v' + plugin.version + '</span>' + | |
| '<span>by ' + plugin.author + '</span>' + | |
| '</div>'; | |
| // Plugin type | |
| const typeDiv = document.createElement('div'); | |
| typeDiv.className = 'plugin-type'; | |
| typeDiv.textContent = plugin.type; | |
| card.appendChild(typeDiv); | |
| // Plugin name | |
| const nameDiv = document.createElement('div'); | |
| nameDiv.className = 'plugin-name'; | |
| nameDiv.textContent = plugin.name; | |
| card.appendChild(nameDiv); | |
| // Plugin description | |
| const descDiv = document.createElement('div'); | |
| descDiv.className = 'plugin-description'; | |
| descDiv.textContent = plugin.description; | |
| card.appendChild(descDiv); | |
| // Tags | |
| const tagsDiv = document.createElement('div'); | |
| tagsDiv.className = 'tags'; | |
| if (Array.isArray(plugin.tags)) { | |
| plugin.tags.forEach(tag => { | |
| const tagSpan = document.createElement('span'); | |
| tagSpan.className = 'tag'; | |
| tagSpan.textContent = tag; | |
| tagsDiv.appendChild(tagSpan); | |
| }); | |
| } | |
| card.appendChild(tagsDiv); | |
| // Meta info | |
| const metaDiv = document.createElement('div'); | |
| metaDiv.className = 'plugin-meta'; | |
| const versionSpan = document.createElement('span'); | |
| versionSpan.textContent = 'v' + plugin.version; | |
| metaDiv.appendChild(versionSpan); | |
| const authorSpan = document.createElement('span'); | |
| authorSpan.textContent = 'by ' + plugin.author; | |
| metaDiv.appendChild(authorSpan); | |
| card.appendChild(metaDiv); |
| const tags = plugin.tags.map(tag => | ||
| '<span class="tag">' + tag + '</span>' | ||
| ).join(''); | ||
| card.innerHTML = '<div class="plugin-type">' + plugin.type + '</div>' + | ||
| '<div class="plugin-name">' + plugin.name + '</div>' + | ||
| '<div class="plugin-description">' + plugin.description + '</div>' + | ||
| '<div class="tags">' + tags + '</div>' + | ||
| '<div class="plugin-meta">' + | ||
| '<span>v' + plugin.version + '</span>' + | ||
| '<span>by ' + plugin.author + '</span>' + | ||
| '</div>'; | ||
There was a problem hiding this comment.
Tag values from marketplace.json are concatenated into HTML without sanitization. This creates an XSS vulnerability if tags contain malicious HTML/JavaScript. Use textContent for text nodes or sanitize the input.
| const tags = plugin.tags.map(tag => | |
| '<spanclass="tag">' + tag + '</span>' | |
| ).join(''); | |
| card.innerHTML = '<divclass="plugin-type">' + plugin.type + '</div>' + | |
| '<divclass="plugin-name">' + plugin.name + '</div>' + | |
| '<divclass="plugin-description">' + plugin.description + '</div>' + | |
| '<divclass="tags">' + tags + '</div>' + | |
| '<divclass="plugin-meta">' + | |
| '<span>v' + plugin.version + '</span>' + | |
| '<span>by ' + plugin.author + '</span>' + | |
| '</div>'; | |
| // Plugin type | |
| const typeDiv = document.createElement('div'); | |
| typeDiv.className = 'plugin-type'; | |
| typeDiv.textContent = plugin.type; | |
| card.appendChild(typeDiv); | |
| // Plugin name | |
| const nameDiv = document.createElement('div'); | |
| nameDiv.className = 'plugin-name'; | |
| nameDiv.textContent = plugin.name; | |
| card.appendChild(nameDiv); | |
| // Plugin description | |
| const descDiv = document.createElement('div'); | |
| descDiv.className = 'plugin-description'; | |
| descDiv.textContent = plugin.description; | |
| card.appendChild(descDiv); | |
| // Tags | |
| const tagsDiv = document.createElement('div'); | |
| tagsDiv.className = 'tags'; | |
| plugin.tags.forEach(tag => { | |
| const tagSpan = document.createElement('span'); | |
| tagSpan.className = 'tag'; | |
| tagSpan.textContent = tag; | |
| tagsDiv.appendChild(tagSpan); | |
| }); | |
| card.appendChild(tagsDiv); | |
| // Plugin meta | |
| const metaDiv = document.createElement('div'); | |
| metaDiv.className = 'plugin-meta'; | |
| const versionSpan = document.createElement('span'); | |
| versionSpan.textContent = 'v' + plugin.version; | |
| metaDiv.appendChild(versionSpan); | |
| const authorSpan = document.createElement('span'); | |
| authorSpan.textContent = 'by ' + plugin.author; | |
| metaDiv.appendChild(authorSpan); | |
| card.appendChild(metaDiv); |
fx
commented
Nov 3, 2025
Closing this PR as it has been superseded by PR #2 (#2). PR #2 includes all valuable content from this PR (LICENSE, .gitignore, GitHub Pages workflow) and follows the correct Claude Code marketplace structure with All changes have been incorporated into PR #2 which is now ready for review and merge. |
Summary
This PR implements Phase 1 of the Claude Code marketplace infrastructure:
Changes
Repository Structure:
Marketplace Manifest:
Test Plugins:
skills/test-skill.md- Verifies skill loading from marketplaceagents/test-agent.md- Verifies agent loading from marketplaceGitHub Pages:
.github/workflows/pages.yml- Automated deployment workflowindex.html- Interactive marketplace browserTesting
After merge:
Next Steps
Phase 2 will update the dotfiles repository to: