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:
Aaron Stannard
2026-08-07 11:59:23 -05:00
committed by GitHub
parent 4332ab42af
commit 5c554e80c8
7 changed files with 312 additions and 17 deletions
+20
View File
@@ -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"
}
]
}
+29
View File
@@ -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": []
}
}
+12 -3
View File
@@ -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
+3 -2
View File
@@ -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`
+18 -6
View File
@@ -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.
+31 -6
View File
@@ -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.
+199
View File
@@ -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}"