mirror of
https://github.com/Aaronontheweb/dotnet-skills.git
synced 2026-09-19 05:04:05 +08:00
feat: add Codex plugin manifest, marketplace, and validation (#76)
Adds a Codex plugin (.codex-plugin/plugin.json) and repo marketplace (.agents/plugins/marketplace.json) alongside the existing Claude Code plugin, so users can install with: codex plugin marketplace add Aaronontheweb/dotnet-skills codex plugin add dotnet-skills@dotnet-skills Skills are auto-discovered from ./skills/ - no skill changes needed. Adds scripts/validate-codex-plugin.sh to enforce frontmatter, version lockstep with the Claude plugin, and marketplace path resolution, wired into CI. Closes #75.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "dotnet-skills",
|
||||
"interface": {
|
||||
"displayName": ".NET Skills"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "dotnet-skills",
|
||||
"source": {
|
||||
"source": "local",
|
||||
"path": "./"
|
||||
},
|
||||
"policy": {
|
||||
"installation": "AVAILABLE",
|
||||
"authentication": "ON_INSTALL"
|
||||
},
|
||||
"category": "Productivity"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "dotnet-skills",
|
||||
"version": "1.4.1",
|
||||
"description": "Professional .NET development skills for Codex - covering C#, F#, Akka.NET, Aspire, testing frameworks, and specialized tools",
|
||||
"author": {
|
||||
"name": "Aaron Stannard",
|
||||
"url": "https://github.com/Aaronontheweb"
|
||||
},
|
||||
"repository": "https://github.com/Aaronontheweb/dotnet-skills",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"dotnet",
|
||||
"csharp",
|
||||
"akka-net",
|
||||
"aspire",
|
||||
"testing",
|
||||
"efcore",
|
||||
"codex"
|
||||
],
|
||||
"skills": "./skills/",
|
||||
"interface": {
|
||||
"displayName": ".NET Skills",
|
||||
"shortDescription": "Professional .NET development skills for C#, Akka.NET, Aspire, and testing",
|
||||
"longDescription": "Battle-tested .NET development patterns from production systems: modern C# coding standards, Akka.NET distributed systems, Aspire cloud-native orchestration, EF Core, testing frameworks, and performance optimization.",
|
||||
"developerName": "Aaron Stannard",
|
||||
"category": "Productivity",
|
||||
"capabilities": []
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,11 @@ jobs:
|
||||
echo "OK: $file"
|
||||
done
|
||||
|
||||
- name: Validate Codex plugin
|
||||
run: |
|
||||
echo "Validating Codex plugin manifest and marketplace..."
|
||||
bash ./scripts/validate-codex-plugin.sh
|
||||
|
||||
- name: Validate YAML frontmatter
|
||||
run: |
|
||||
echo "Checking YAML frontmatter in skills..."
|
||||
@@ -163,12 +168,16 @@ jobs:
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Add the marketplace
|
||||
# Claude Code - add the marketplace
|
||||
/plugin marketplace add Aaronontheweb/dotnet-skills
|
||||
|
||||
# Install the plugin (includes all skills and agents)
|
||||
/plugin install dotnet-skills
|
||||
```
|
||||
|
||||
```bash
|
||||
# Codex - add the marketplace, then install the plugin
|
||||
codex plugin marketplace add Aaronontheweb/dotnet-skills
|
||||
codex plugin add dotnet-skills@dotnet-skills
|
||||
```
|
||||
INSTALL
|
||||
|
||||
# Changes since last tag
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# AGENTS.md
|
||||
|
||||
This repo supports both Claude Code and OpenCode.
|
||||
This repo supports Claude Code, Codex, and OpenCode.
|
||||
|
||||
When adding/removing skills or agents, keep the router/index snippets up to date so downstream repos can copy/paste them.
|
||||
|
||||
@@ -10,4 +10,5 @@ Reference:
|
||||
Maintenance:
|
||||
1. Update `.claude-plugin/plugin.json`
|
||||
2. Run `./scripts/validate-marketplace.sh`
|
||||
3. Regenerate the compressed index: `./scripts/generate-skill-index-snippets.sh`
|
||||
3. Run `./scripts/validate-codex-plugin.sh` (Codex manifest + marketplace, version lockstep with Claude plugin)
|
||||
4. Regenerate the compressed index: `./scripts/generate-skill-index-snippets.sh`
|
||||
|
||||
@@ -15,15 +15,20 @@ This is a knowledge base repository - not a traditional code project. There is n
|
||||
```
|
||||
dotnet-skills/
|
||||
├── .claude-plugin/
|
||||
│ ├── marketplace.json # Marketplace catalog
|
||||
│ └── plugin.json # Plugin metadata + skill/agent registry
|
||||
│ ├── marketplace.json # Claude Code marketplace catalog
|
||||
│ └── plugin.json # Claude Code plugin metadata + skill/agent registry
|
||||
├── .codex-plugin/
|
||||
│ └── plugin.json # Codex plugin metadata (skills auto-discovered from ./skills/)
|
||||
├── .agents/
|
||||
│ └── plugins/
|
||||
│ └── marketplace.json # Codex marketplace
|
||||
├── skills/ # Flat structure for Copilot compatibility
|
||||
│ ├── akka-best-practices/SKILL.md
|
||||
│ ├── aspire-integration-testing/SKILL.md
|
||||
│ ├── csharp-coding-standards/SKILL.md
|
||||
│ ├── testcontainers/SKILL.md
|
||||
│ └── ...
|
||||
├── agents/ # Agent definitions (flat .md files)
|
||||
├── agents/ # Agent definitions (flat .md files, Claude Code only)
|
||||
└── scripts/ # Validation and sync scripts
|
||||
```
|
||||
|
||||
@@ -63,9 +68,11 @@ color: purple # optional
|
||||
- Use appropriate prefix for framework-specific skills (see naming convention above)
|
||||
- No prefix for general .NET skills
|
||||
2. Add the skill path to `.claude-plugin/plugin.json` in the `skills` array
|
||||
- The Codex plugin (`.codex-plugin/plugin.json`) auto-discovers the whole `skills/` dir — no manifest change needed there
|
||||
3. Run `./scripts/validate-marketplace.sh` to verify
|
||||
4. Run `./scripts/generate-skill-index-snippets.sh --update-readme` to regenerate the compressed index
|
||||
5. Commit all changes together (SKILL.md, plugin.json, and README.md)
|
||||
4. Run `./scripts/validate-codex-plugin.sh` to verify the Codex plugin (frontmatter, version lockstep)
|
||||
5. Run `./scripts/generate-skill-index-snippets.sh --update-readme` to regenerate the compressed index
|
||||
6. Commit all changes together (SKILL.md, plugin.json, and README.md)
|
||||
|
||||
### Adding Skills to Index Categories
|
||||
|
||||
@@ -82,14 +89,19 @@ When adding a skill with a **new prefix pattern**, update `scripts/generate-skil
|
||||
## Marketplace Publishing
|
||||
|
||||
**To publish a release:**
|
||||
1. Update version in `.claude-plugin/plugin.json`
|
||||
1. Update version in `.claude-plugin/plugin.json` **and** `.codex-plugin/plugin.json` — they must match (CI enforces this via `scripts/validate-codex-plugin.sh`)
|
||||
2. Push a semver tag: `git tag v1.0.0 && git push origin v1.0.0`
|
||||
3. GitHub Actions creates the release automatically
|
||||
|
||||
**Users install with:**
|
||||
```bash
|
||||
# Claude Code
|
||||
/plugin marketplace add Aaronontheweb/dotnet-skills
|
||||
/plugin install dotnet-skills
|
||||
|
||||
# Codex
|
||||
codex plugin marketplace add Aaronontheweb/dotnet-skills
|
||||
codex plugin add dotnet-skills@dotnet-skills
|
||||
```
|
||||
|
||||
See `skills/marketplace-publishing/SKILL.md` for detailed workflow.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# .NET Skills for Claude Code
|
||||
# .NET Skills for Claude Code and Codex
|
||||
|
||||
A comprehensive Claude Code plugin with **30 skills** and **5 specialized agents** for professional .NET development. Battle-tested patterns from production systems covering C#, Akka.NET, Aspire, EF Core, testing, and performance optimization.
|
||||
A comprehensive AI coding plugin with **30 skills** and **5 specialized agents** for professional .NET development. Battle-tested patterns from production systems covering C#, Akka.NET, Aspire, EF Core, testing, and performance optimization.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -22,6 +22,24 @@ To update:
|
||||
/plugin marketplace update
|
||||
```
|
||||
|
||||
### Codex (OpenAI)
|
||||
|
||||
[Official Docs](https://developers.openai.com/codex/plugins)
|
||||
|
||||
Add the marketplace, then install the plugin:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add Aaronontheweb/dotnet-skills
|
||||
codex plugin add dotnet-skills@dotnet-skills
|
||||
```
|
||||
|
||||
Verify the install:
|
||||
```bash
|
||||
codex plugin list
|
||||
```
|
||||
|
||||
Skills are auto-discovered from the plugin's `skills/` directory. Invoke one with `/skills` or `$skill-name` in the Codex CLI or IDE extension. Note: the specialized agents are Claude Code-specific and are not included in the Codex plugin.
|
||||
|
||||
### GitHub Copilot
|
||||
|
||||
[Official Docs](https://docs.github.com/en/copilot/concepts/agents/about-agent-skills)
|
||||
@@ -235,8 +253,14 @@ These skills emphasize patterns that work in production:
|
||||
```
|
||||
dotnet-skills/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json # Plugin manifest
|
||||
├── agents/ # 5 specialized agents
|
||||
│ ├── plugin.json # Claude Code plugin manifest
|
||||
│ └── marketplace.json # Claude Code marketplace
|
||||
├── .codex-plugin/
|
||||
│ └── plugin.json # Codex plugin manifest
|
||||
├── .agents/
|
||||
│ └── plugins/
|
||||
│ └── marketplace.json # Codex marketplace
|
||||
├── agents/ # 5 specialized agents (Claude Code)
|
||||
│ ├── akka-net-specialist.md
|
||||
│ ├── docfx-specialist.md
|
||||
│ ├── dotnet-benchmark-designer.md
|
||||
@@ -260,8 +284,9 @@ dotnet-skills/
|
||||
Want to add a skill or agent? PRs welcome!
|
||||
|
||||
1. Create `skills/<skill-name>/SKILL.md` (use prefixes like `akka-`, `aspire-`, `csharp-` for category)
|
||||
2. Add the path to `.claude-plugin/plugin.json`
|
||||
3. Submit a PR
|
||||
2. Add the path to `.claude-plugin/plugin.json` (the Codex plugin auto-discovers the whole `skills/` dir)
|
||||
3. Run `./scripts/validate-marketplace.sh` and `./scripts/validate-codex-plugin.sh`
|
||||
4. Submit a PR
|
||||
|
||||
Skills should be comprehensive reference documents (10-40KB) with concrete examples and anti-patterns.
|
||||
|
||||
|
||||
Executable
+199
@@ -0,0 +1,199 @@
|
||||
#!/bin/bash
|
||||
# Validates the Codex plugin (.codex-plugin/plugin.json) and repo
|
||||
# marketplace (.agents/plugins/marketplace.json) for consistency with
|
||||
# the skills on disk and lockstep with the Claude plugin.
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
CODEX_PLUGIN_JSON="$REPO_ROOT/.codex-plugin/plugin.json"
|
||||
CODEX_MARKETPLACE_JSON="$REPO_ROOT/.agents/plugins/marketplace.json"
|
||||
CLAUDE_PLUGIN_JSON="$REPO_ROOT/.claude-plugin/plugin.json"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
errors=0
|
||||
warnings=0
|
||||
|
||||
echo "Validating Codex plugin structure..."
|
||||
echo ""
|
||||
|
||||
# Check JSON syntax
|
||||
for f in "$CODEX_PLUGIN_JSON" "$CODEX_MARKETPLACE_JSON"; do
|
||||
if [ ! -f "$f" ]; then
|
||||
echo -e "${RED}ERROR: Missing file: $f${NC}"
|
||||
exit 1
|
||||
fi
|
||||
if ! jq . "$f" > /dev/null 2>&1; then
|
||||
echo -e "${RED}ERROR: Invalid JSON syntax in $f${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}$(basename "$f") syntax: OK${NC}"
|
||||
done
|
||||
|
||||
# Check required plugin manifest fields
|
||||
echo ""
|
||||
echo "Checking plugin manifest fields..."
|
||||
for field in name version description skills; do
|
||||
if ! jq -e ".$field" "$CODEX_PLUGIN_JSON" > /dev/null 2>&1; then
|
||||
echo -e "${RED}ERROR: Missing required field '$field' in .codex-plugin/plugin.json${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
done
|
||||
|
||||
# Plugin name must be kebab-case
|
||||
plugin_name=$(jq -r '.name' "$CODEX_PLUGIN_JSON")
|
||||
if ! echo "$plugin_name" | grep -qE '^[a-z0-9]+(-[a-z0-9]+)*$'; then
|
||||
echo -e "${RED}ERROR: Plugin name '$plugin_name' is not kebab-case${NC}"
|
||||
((++errors))
|
||||
else
|
||||
echo -e "${GREEN}Plugin name: $plugin_name${NC}"
|
||||
fi
|
||||
|
||||
# Version must be semver
|
||||
plugin_version=$(jq -r '.version' "$CODEX_PLUGIN_JSON")
|
||||
if ! echo "$plugin_version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
|
||||
echo -e "${RED}ERROR: Plugin version '$plugin_version' is not semver${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
|
||||
# Version must match the Claude plugin (lockstep releases)
|
||||
if [ -f "$CLAUDE_PLUGIN_JSON" ]; then
|
||||
claude_version=$(jq -r '.version' "$CLAUDE_PLUGIN_JSON")
|
||||
if [ "$plugin_version" != "$claude_version" ]; then
|
||||
echo -e "${RED}ERROR: Version mismatch - Codex: $plugin_version, Claude: $claude_version${NC}"
|
||||
echo -e "${RED} Both manifests must be bumped together in lockstep.${NC}"
|
||||
((++errors))
|
||||
else
|
||||
echo -e "${GREEN}Version lockstep OK: $plugin_version${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check skills directory reference
|
||||
echo ""
|
||||
echo "Checking skills..."
|
||||
skills_value=$(jq -r '.skills' "$CODEX_PLUGIN_JSON")
|
||||
clean_skills="${skills_value#./}"
|
||||
skills_dir="$REPO_ROOT/$clean_skills"
|
||||
|
||||
if [ ! -d "$skills_dir" ]; then
|
||||
echo -e "${RED}ERROR: Skills directory not found: $skills_dir${NC}"
|
||||
((++errors))
|
||||
else
|
||||
skill_count=$(find "$skills_dir" -maxdepth 2 -name "SKILL.md" 2>/dev/null | wc -l)
|
||||
if [ "$skill_count" -eq 0 ]; then
|
||||
echo -e "${RED}ERROR: No SKILL.md files found under $skills_dir${NC}"
|
||||
((++errors))
|
||||
else
|
||||
echo -e "${GREEN}Skills directory OK: $skills_value ($skill_count SKILL.md files)${NC}"
|
||||
fi
|
||||
|
||||
# Check YAML frontmatter (name + description required by Codex spec)
|
||||
while IFS= read -r file; do
|
||||
if ! head -1 "$file" | grep -q "^---$"; then
|
||||
echo -e "${RED}ERROR: Missing YAML frontmatter in $file${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
if ! grep -q "^name:" "$file"; then
|
||||
echo -e "${RED}ERROR: Missing 'name' in $file${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
if ! grep -q "^description:" "$file"; then
|
||||
echo -e "${RED}ERROR: Missing 'description' in $file${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
done < <(find "$skills_dir" -maxdepth 2 -name "SKILL.md" 2>/dev/null)
|
||||
fi
|
||||
|
||||
# Check marketplace structure
|
||||
echo ""
|
||||
echo "Checking marketplace..."
|
||||
if ! jq -e '.name' "$CODEX_MARKETPLACE_JSON" > /dev/null 2>&1; then
|
||||
echo -e "${RED}ERROR: Marketplace missing 'name'${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
|
||||
plugin_count=$(jq '.plugins | length' "$CODEX_MARKETPLACE_JSON" 2>/dev/null || echo 0)
|
||||
if [ "$plugin_count" -eq 0 ]; then
|
||||
echo -e "${RED}ERROR: Marketplace has no plugins${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
|
||||
# Each plugin entry must resolve to a dir containing .codex-plugin/plugin.json
|
||||
while IFS= read -r entry; do
|
||||
entry_name=$(echo "$entry" | jq -r '.name')
|
||||
entry_path=$(echo "$entry" | jq -r '.source.path // empty')
|
||||
entry_source=$(echo "$entry" | jq -r '.source.source // empty')
|
||||
entry_install=$(echo "$entry" | jq -r '.policy.installation // empty')
|
||||
entry_auth=$(echo "$entry" | jq -r '.policy.authentication // empty')
|
||||
entry_category=$(echo "$entry" | jq -r '.category // empty')
|
||||
|
||||
if [ -z "$entry_name" ]; then
|
||||
echo -e "${RED}ERROR: Marketplace plugin entry missing 'name'${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
if [ "$entry_source" != "local" ]; then
|
||||
echo -e "${RED}ERROR: Plugin '$entry_name' source must be 'local', got '$entry_source'${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
if ! echo "$entry_path" | grep -q '^\./'; then
|
||||
echo -e "${RED}ERROR: Plugin '$entry_name' source.path must start with './'${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
if [ -z "$entry_install" ] || [ -z "$entry_auth" ] || [ -z "$entry_category" ]; then
|
||||
echo -e "${RED}ERROR: Plugin '$entry_name' must set policy.installation, policy.authentication, and category${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
|
||||
# Resolve path relative to marketplace root (repo root, not .agents/plugins/)
|
||||
if [ -n "$entry_path" ]; then
|
||||
clean_path="${entry_path#./}"
|
||||
resolved="$REPO_ROOT/$clean_path"
|
||||
if [ ! -f "$resolved/.codex-plugin/plugin.json" ]; then
|
||||
echo -e "${RED}ERROR: Plugin '$entry_name' path '$entry_path' has no .codex-plugin/plugin.json${NC}"
|
||||
((++errors))
|
||||
else
|
||||
echo -e "${GREEN}OK: plugin '$entry_name' -> $entry_path${NC}"
|
||||
fi
|
||||
fi
|
||||
done < <(jq -c '.plugins[]' "$CODEX_MARKETPLACE_JSON")
|
||||
|
||||
# Cross-check skill parity with the Claude manifest: every skill registered
|
||||
# there must exist on disk (Codex auto-includes the whole skills dir, so the
|
||||
# Claude manifest is the one that can drift).
|
||||
echo ""
|
||||
echo "Checking Claude manifest parity..."
|
||||
if [ -f "$CLAUDE_PLUGIN_JSON" ]; then
|
||||
claude_skills=$(jq -r '.skills[]' "$CLAUDE_PLUGIN_JSON" 2>/dev/null)
|
||||
if [ -n "$claude_skills" ]; then
|
||||
while IFS= read -r source; do
|
||||
clean_source="${source#./}"
|
||||
if [ ! -f "$REPO_ROOT/$clean_source/SKILL.md" ]; then
|
||||
echo -e "${RED}ERROR: Claude manifest references missing skill: $source${NC}"
|
||||
((++errors))
|
||||
fi
|
||||
done <<< "$claude_skills"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "=== Summary ==="
|
||||
echo "Codex plugin: $plugin_name@$plugin_version"
|
||||
echo "Skills: $(jq -r '.skills' "$CODEX_PLUGIN_JSON")"
|
||||
echo "Marketplace plugins: $plugin_count"
|
||||
|
||||
if [ $errors -gt 0 ]; then
|
||||
echo -e "${RED}Errors: $errors${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $warnings -gt 0 ]; then
|
||||
echo -e "${YELLOW}Warnings: $warnings${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Codex validation passed!${NC}"
|
||||
Reference in New Issue
Block a user