Skip to content

Commit 8ac0e41

Browse files
authored
Merge pull request #750 from github/plugin-migration
refactor: migrate plugins to Claude Code spec format
2 parents 84b44ca + e20084d commit 8ac0e41

File tree

245 files changed

+920
-1835
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

245 files changed

+920
-1835
lines changed

.github/workflows/check-line-endings.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Check Line Endings
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [staged]
66
pull_request:
7-
branches: [main]
7+
branches: [staged]
88

99
permissions:
1010
contents: read
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
name: Check Plugin Structure
2+
3+
on:
4+
pull_request:
5+
branches: [staged]
6+
paths:
7+
- "plugins/**"
8+
9+
permissions:
10+
contents: read
11+
pull-requests: write
12+
13+
jobs:
14+
check-materialized-files:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Check for materialized files in plugin directories
21+
uses: actions/github-script@v7
22+
with:
23+
script: |
24+
const { execSync } = require('child_process');
25+
const fs = require('fs');
26+
const path = require('path');
27+
28+
const pluginsDir = 'plugins';
29+
const errors = [];
30+
31+
if (!fs.existsSync(pluginsDir)) {
32+
console.log('No plugins directory found');
33+
return;
34+
}
35+
36+
const pluginDirs = fs.readdirSync(pluginsDir, { withFileTypes: true })
37+
.filter(d => d.isDirectory())
38+
.map(d => d.name);
39+
40+
for (const plugin of pluginDirs) {
41+
const pluginPath = path.join(pluginsDir, plugin);
42+
43+
// Check for materialized agent/command/skill files
44+
for (const subdir of ['agents', 'commands', 'skills']) {
45+
const subdirPath = path.join(pluginPath, subdir);
46+
if (!fs.existsSync(subdirPath)) continue;
47+
48+
const stat = fs.lstatSync(subdirPath);
49+
if (stat.isSymbolicLink()) {
50+
errors.push(`${pluginPath}/${subdir} is a symlink — symlinks should not exist in plugin directories`);
51+
continue;
52+
}
53+
54+
if (stat.isDirectory()) {
55+
const files = fs.readdirSync(subdirPath);
56+
if (files.length > 0) {
57+
errors.push(
58+
`${pluginPath}/${subdir}/ contains ${files.length} file(s): ${files.join(', ')}. ` +
59+
`Plugin directories on staged should only contain .github/plugin/plugin.json and README.md. ` +
60+
`Agent, command, and skill files are materialized automatically during publish to main.`
61+
);
62+
}
63+
}
64+
}
65+
66+
// Check for symlinks anywhere in the plugin directory
67+
try {
68+
const allFiles = execSync(`find "${pluginPath}" -type l`, { encoding: 'utf-8' }).trim();
69+
if (allFiles) {
70+
errors.push(`${pluginPath} contains symlinks:\n${allFiles}`);
71+
}
72+
} catch (e) {
73+
// find returns non-zero if no matches, ignore
74+
}
75+
}
76+
77+
if (errors.length > 0) {
78+
const prBranch = context.payload.pull_request.head.ref;
79+
const prRepo = context.payload.pull_request.head.repo.full_name;
80+
const isFork = context.payload.pull_request.head.repo.fork;
81+
82+
const body = [
83+
'⚠️ **Materialized files or symlinks detected in plugin directories**',
84+
'',
85+
'Plugin directories on the `staged` branch should only contain:',
86+
'- `.github/plugin/plugin.json` (metadata)',
87+
'- `README.md`',
88+
'',
89+
'Agent, command, and skill files are copied in automatically when publishing to `main`.',
90+
'',
91+
'**Issues found:**',
92+
...errors.map(e => `- ${e}`),
93+
'',
94+
'---',
95+
'',
96+
'### How to fix',
97+
'',
98+
'It looks like your branch may be based on `main` (which contains materialized files). Here are two options:',
99+
'',
100+
'**Option 1: Rebase onto `staged`** (recommended if you have few commits)',
101+
'```bash',
102+
`git fetch origin staged`,
103+
`git rebase --onto origin/staged origin/main ${prBranch}`,
104+
`git push --force-with-lease`,
105+
'```',
106+
'',
107+
'**Option 2: Remove the extra files manually**',
108+
'```bash',
109+
'# Remove materialized files from plugin directories',
110+
'find plugins/ -mindepth 2 -maxdepth 2 -type d \\( -name agents -o -name commands -o -name skills \\) -exec rm -rf {} +',
111+
'# Remove any symlinks',
112+
'find plugins/ -type l -delete',
113+
'git add -A && git commit -m "fix: remove materialized plugin files"',
114+
'git push',
115+
'```',
116+
].join('\n');
117+
118+
await github.rest.pulls.createReview({
119+
owner: context.repo.owner,
120+
repo: context.repo.repo,
121+
pull_number: context.issue.number,
122+
event: 'REQUEST_CHANGES',
123+
body
124+
});
125+
126+
core.setFailed('Plugin directories contain materialized files or symlinks that should not be on staged');
127+
} else {
128+
console.log('✅ All plugin directories are clean');
129+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Check PR Target Branch
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
types: [opened]
7+
8+
permissions:
9+
pull-requests: write
10+
11+
jobs:
12+
check-target:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Reject PR targeting main
16+
uses: actions/github-script@v7
17+
with:
18+
script: |
19+
const body = [
20+
'⚠️ **This PR targets `main`, but PRs should target `staged`.**',
21+
'',
22+
'The `main` branch is auto-published from `staged` and should not receive direct PRs.',
23+
'Please close this PR and re-open it against the `staged` branch.',
24+
'',
25+
'You can change the base branch using the **Edit** button at the top of this PR,',
26+
'or run: `gh pr edit ${{ github.event.pull_request.number }} --base staged`'
27+
].join('\n');
28+
29+
await github.rest.pulls.createReview({
30+
owner: context.repo.owner,
31+
repo: context.repo.repo,
32+
pull_number: context.issue.number,
33+
event: 'REQUEST_CHANGES',
34+
body
35+
});

.github/workflows/codespell.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Check Spelling
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [staged]
66
pull_request:
7-
branches: [main]
7+
branches: [staged]
88

99
permissions:
1010
contents: read

.github/workflows/publish.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Publish to main
2+
3+
on:
4+
push:
5+
branches: [staged]
6+
7+
concurrency:
8+
group: publish-to-main
9+
cancel-in-progress: true
10+
11+
permissions:
12+
contents: write
13+
14+
jobs:
15+
publish:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout staged branch
19+
uses: actions/checkout@v4
20+
with:
21+
ref: staged
22+
fetch-depth: 0
23+
24+
- name: Extract Node version from package.json
25+
id: node-version
26+
run: |
27+
NODE_VERSION=$(jq -r '.engines.node // "22"' package.json)
28+
echo "version=${NODE_VERSION}" >> "$GITHUB_OUTPUT"
29+
30+
- name: Setup Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: ${{ steps.node-version.outputs.version }}
34+
35+
- name: Install dependencies
36+
run: npm ci
37+
38+
- name: Materialize plugin files
39+
run: node eng/materialize-plugins.mjs
40+
41+
- name: Build generated files
42+
run: npm run build
43+
44+
- name: Fix line endings
45+
run: bash scripts/fix-line-endings.sh
46+
47+
- name: Publish to main
48+
run: |
49+
git config user.name "github-actions[bot]"
50+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
51+
git add -A
52+
git commit -m "chore: publish from staged [skip ci]" --allow-empty
53+
git push origin HEAD:main --force

.github/workflows/validate-readme.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: Validate README.md
22

33
on:
44
pull_request:
5+
branches: [staged]
56
types: [opened, synchronize, reopened]
67
paths:
78
- "instructions/**"

AGENTS.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The Awesome GitHub Copilot repository is a community-driven collection of custom
99
- **Instructions** - Coding standards and best practices applied to specific file patterns
1010
- **Skills** - Self-contained folders with instructions and bundled resources for specialized tasks
1111
- **Hooks** - Automated workflows triggered by specific events during development
12-
- **Plugins** - Installable packages that group related agents, prompts, and skills around specific themes
12+
- **Plugins** - Installable packages that group related agents, commands, and skills around specific themes
1313

1414
## Repository Structure
1515

@@ -101,7 +101,7 @@ All agent files (`*.agent.md`), prompt files (`*.prompt.md`), and instruction fi
101101
- plugin.json must have `name` field (matching the folder name)
102102
- plugin.json must have `description` field (describing the plugin's purpose)
103103
- plugin.json must have `version` field (semantic version, e.g., "1.0.0")
104-
- Plugin folders can contain any combination of agents, prompts, instructions, skills, and hooks
104+
- Plugin content is defined declaratively in plugin.json using Claude Code spec fields (`agents`, `commands`, `skills`). Source files live in top-level directories and are materialized into plugins by CI.
105105
- The `marketplace.json` file is automatically generated from all plugins during build
106106
- Plugins are discoverable and installable via GitHub Copilot CLI
107107

@@ -135,7 +135,7 @@ When adding a new agent, prompt, instruction, skill, hook, or plugin:
135135

136136
**For Plugins:**
137137
1. Run `npm run plugin:create -- --name <plugin-name>` to scaffold a new plugin
138-
2. Add agents, prompts, skills, or hooks to the plugin folder
138+
2. Define agents, commands, and skills in `plugin.json` using Claude Code spec fields
139139
3. Edit the generated `plugin.json` with your metadata
140140
4. Run `npm run plugin:validate` to validate the plugin structure
141141
5. Run `npm run build` to update README.md and marketplace.json
@@ -179,6 +179,8 @@ Before committing:
179179

180180
When creating a pull request:
181181

182+
> **Important:** All pull requests should target the **`staged`** branch, not `main`.
183+
182184
1. **README updates**: New files should automatically be added to the README when you run `npm run build`
183185
2. **Front matter validation**: Ensure all markdown files have the required front matter fields
184186
3. **File naming**: Verify all new files follow the lower-case-with-hyphens naming convention
@@ -246,9 +248,8 @@ For plugins (plugins/*/):
246248
- [ ] `plugin.json` has non-empty `description` field
247249
- [ ] `plugin.json` has `version` field (semantic version, e.g., "1.0.0")
248250
- [ ] Directory name is lower case with hyphens
249-
- [ ] If `tags` is present, it is an array of lowercase hyphenated strings
250-
- [ ] If `items` is present, each item has `path` and `kind` fields
251-
- [ ] The `kind` field value is one of: `prompt`, `agent`, `instruction`, `skill`, or `hook`
251+
- [ ] If `keywords` is present, it is an array of lowercase hyphenated strings
252+
- [ ] If `agents`, `commands`, or `skills` arrays are present, each entry is a valid relative path
252253
- [ ] The plugin does not reference non-existent files
253254
- [ ] Run `npm run build` to verify marketplace.json is updated correctly
254255

0 commit comments

Comments
 (0)