chore: publish from main
@@ -9,7 +9,7 @@ The Awesome GitHub Copilot repository is a community-driven collection of custom
|
|||||||
- **Skills** - Self-contained folders with instructions and bundled resources for specialized tasks
|
- **Skills** - Self-contained folders with instructions and bundled resources for specialized tasks
|
||||||
- **Hooks** - Automated workflows triggered by specific events during development
|
- **Hooks** - Automated workflows triggered by specific events during development
|
||||||
- **Workflows** - [Agentic Workflows](https://github.github.com/gh-aw) for AI-powered repository automation in GitHub Actions
|
- **Workflows** - [Agentic Workflows](https://github.github.com/gh-aw) for AI-powered repository automation in GitHub Actions
|
||||||
- **Plugins** - Installable packages that group related agents, commands, and skills around specific themes
|
- **Plugins** - Installable packages that group related agents, hooks, and skills around specific themes
|
||||||
|
|
||||||
## Repository Structure
|
## Repository Structure
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ All agent files (`*.agent.md`) and instruction files (`*.instructions.md`) must
|
|||||||
- plugin.json must have `name` field (matching the folder name)
|
- plugin.json must have `name` field (matching the folder name)
|
||||||
- plugin.json must have `description` field (describing the plugin's purpose)
|
- plugin.json must have `description` field (describing the plugin's purpose)
|
||||||
- plugin.json must have `version` field (semantic version, e.g., "1.0.0")
|
- plugin.json must have `version` field (semantic version, e.g., "1.0.0")
|
||||||
- Plugin content is defined declaratively in plugin.json under `extensions.com.github.awesome-copilot` using source-only composition fields (`agents`, `commands`, `hooks`, `skills`, and `extensions`). Source files live in top-level directories and are materialized into plugins by CI. This namespace is stripped from the served manifest — conventional directory discovery handles the materialized content in spec mode.
|
- Plugin content is defined declaratively in plugin.json under `extensions.com.github.awesome-copilot` using source-only composition fields (`agents`, `hooks`, `skills`, and `extensions`). Source files live in top-level directories and are materialized into plugins by CI. This namespace is stripped from the served manifest — skills use the standard `skills/` directory and Copilot-specific content uses `com.github.copilot/`.
|
||||||
- The `marketplace.json` file is automatically generated from all plugins during build
|
- The `marketplace.json` file is automatically generated from all plugins during build
|
||||||
- Plugins are discoverable and installable via GitHub Copilot CLI
|
- Plugins are discoverable and installable via GitHub Copilot CLI
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ When adding a new agent, instruction, skill, hook, workflow, or plugin:
|
|||||||
**For Plugins:**
|
**For Plugins:**
|
||||||
|
|
||||||
1. Run `npm run plugin:create -- --name <plugin-name>` to scaffold a new plugin
|
1. Run `npm run plugin:create -- --name <plugin-name>` to scaffold a new plugin
|
||||||
2. Define agents, commands, hooks, skills, and reusable extensions under `extensions.com.github.awesome-copilot` in `plugin.json`
|
2. Define agents, hooks, skills, and reusable extensions under `extensions.com.github.awesome-copilot` in `plugin.json`
|
||||||
3. Edit the generated `plugin.json` with your metadata
|
3. Edit the generated `plugin.json` with your metadata
|
||||||
4. Run `npm run plugin:validate` to validate the plugin structure
|
4. Run `npm run plugin:validate` to validate the plugin structure
|
||||||
5. Run `npm run build` to update README.md and marketplace.json
|
5. Run `npm run build` to update README.md and marketplace.json
|
||||||
|
|||||||
@@ -153,11 +153,11 @@ Canvas extensions live in `extensions/<extension-id>/` as reusable source compon
|
|||||||
|
|
||||||
### Adding Plugins
|
### Adding Plugins
|
||||||
|
|
||||||
Plugins group related agents, commands, and skills around specific themes or workflows, making it easy for users to install comprehensive toolkits via GitHub Copilot CLI.
|
Plugins group related agents, hooks, and skills around specific themes or workflows, making it easy for users to install comprehensive toolkits via GitHub Copilot CLI.
|
||||||
|
|
||||||
1. **Create your plugin**: Run `npm run plugin:create` to scaffold a new plugin
|
1. **Create your plugin**: Run `npm run plugin:create` to scaffold a new plugin
|
||||||
2. **Follow the naming convention**: Use descriptive, lowercase folder names with hyphens (e.g., `python-web-development`)
|
2. **Follow the naming convention**: Use descriptive, lowercase folder names with hyphens (e.g., `python-web-development`)
|
||||||
3. **Define your content**: List agents, commands, hooks, skills, and reusable extensions under `extensions.com.github.awesome-copilot` in `plugin.json`
|
3. **Define your content**: List agents, hooks, skills, and reusable extensions under `extensions.com.github.awesome-copilot` in `plugin.json`
|
||||||
4. **Test your plugin**: Run `npm run plugin:validate` to verify your plugin structure
|
4. **Test your plugin**: Run `npm run plugin:validate` to verify your plugin structure
|
||||||
|
|
||||||
#### Creating a plugin
|
#### Creating a plugin
|
||||||
@@ -174,7 +174,7 @@ plugins/my-plugin-id/
|
|||||||
└── README.md # Plugin documentation
|
└── README.md # Plugin documentation
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** Plugin content is defined declaratively in plugin.json under `extensions.com.github.awesome-copilot`. Source files live in top-level directories and are materialized into plugins by CI. This repository namespace is removed from the served manifest.
|
> **Note:** Plugin content is defined declaratively in plugin.json under `extensions.com.github.awesome-copilot`. Source files live in top-level directories and are materialized into plugins by CI. Skills are emitted under `skills/`; Copilot-specific agents, hooks, and extensions are emitted under `com.github.copilot/`. This repository namespace is removed from the served manifest.
|
||||||
|
|
||||||
#### plugin.json example
|
#### plugin.json example
|
||||||
|
|
||||||
@@ -191,7 +191,6 @@ plugins/my-plugin-id/
|
|||||||
"extensions": {
|
"extensions": {
|
||||||
"com.github.awesome-copilot": {
|
"com.github.awesome-copilot": {
|
||||||
"agents": ["./agents/my-agent.md"],
|
"agents": ["./agents/my-agent.md"],
|
||||||
"commands": ["./commands/my-command.md"],
|
|
||||||
"skills": ["./skills/my-skill/"]
|
"skills": ["./skills/my-skill/"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +199,7 @@ plugins/my-plugin-id/
|
|||||||
|
|
||||||
#### Plugin Guidelines
|
#### Plugin Guidelines
|
||||||
|
|
||||||
- **Declarative content**: Plugin content is specified under `extensions.com.github.awesome-copilot` — source files live in top-level directories and are materialized into plugins by CI
|
- **Declarative content**: Plugin content is specified under `extensions.com.github.awesome-copilot` — source files live in top-level directories and are materialized into plugins by CI. Skills use the standard `skills/` directory; Copilot-specific content uses `com.github.copilot/`.
|
||||||
- **Valid references**: All paths referenced in plugin.json must point to existing source files in the repository
|
- **Valid references**: All paths referenced in plugin.json must point to existing source files in the repository
|
||||||
- **Reusable extensions**: Curated plugins can bundle extensions by adding `./extensions/<name>` paths under `extensions.com.github.awesome-copilot.extensions`; the same extension can be listed by multiple plugins
|
- **Reusable extensions**: Curated plugins can bundle extensions by adding `./extensions/<name>` paths under `extensions.com.github.awesome-copilot.extensions`; the same extension can be listed by multiple plugins
|
||||||
- **Instructions excluded**: Instructions are standalone resources and are not part of plugins
|
- **Instructions excluded**: Instructions are standalone resources and are not part of plugins
|
||||||
|
|||||||
@@ -7,25 +7,35 @@ import { ROOT_FOLDER } from "./constants.mjs";
|
|||||||
|
|
||||||
const PLUGINS_DIR = path.join(ROOT_FOLDER, "plugins");
|
const PLUGINS_DIR = path.join(ROOT_FOLDER, "plugins");
|
||||||
const EXTENSIONS_DIR = path.join(ROOT_FOLDER, "extensions");
|
const EXTENSIONS_DIR = path.join(ROOT_FOLDER, "extensions");
|
||||||
|
const COPILOT_CONTENT_DIR = "com.github.copilot";
|
||||||
|
const AWESOME_COPILOT_NAMESPACE = "com.github.awesome-copilot";
|
||||||
const MATERIALIZED_SPECS = {
|
const MATERIALIZED_SPECS = {
|
||||||
agents: {
|
agents: {
|
||||||
path: "agents",
|
paths: [path.join(COPILOT_CONTENT_DIR, "agents"), "agents"],
|
||||||
restore(dirPath) {
|
restore(dirPath) {
|
||||||
return collectFiles(dirPath).map((relativePath) => `./agents/${relativePath}`);
|
return collectFiles(dirPath).map((relativePath) => `./agents/${relativePath}`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
commands: {
|
hooks: {
|
||||||
path: "commands",
|
paths: [path.join(COPILOT_CONTENT_DIR, "hooks"), "hooks"],
|
||||||
restore(dirPath) {
|
restore(dirPath) {
|
||||||
return collectFiles(dirPath).map((relativePath) => `./commands/${relativePath}`);
|
return collectDirectoriesContainingFile(dirPath, "hooks.json")
|
||||||
|
.map((relativePath) => `./hooks/${relativePath}/`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
skills: {
|
skills: {
|
||||||
path: "skills",
|
paths: ["skills"],
|
||||||
restore(dirPath) {
|
restore(dirPath) {
|
||||||
return collectSkillDirectories(dirPath).map((relativePath) => `./skills/${relativePath}/`);
|
return collectSkillDirectories(dirPath).map((relativePath) => `./skills/${relativePath}/`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
extensions: {
|
||||||
|
paths: [path.join(COPILOT_CONTENT_DIR, "extensions"), "extensions"],
|
||||||
|
restore(dirPath) {
|
||||||
|
return collectDirectoriesContainingFile(dirPath, "extension.mjs")
|
||||||
|
.map((relativePath) => `./extensions/${relativePath}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function copyDirRecursive(src, dest) {
|
function copyDirRecursive(src, dest) {
|
||||||
@@ -78,22 +88,19 @@ export function restoreManifestFromMaterializedFiles(pluginPath) {
|
|||||||
|
|
||||||
let changed = false;
|
let changed = false;
|
||||||
for (const [field, spec] of Object.entries(MATERIALIZED_SPECS)) {
|
for (const [field, spec] of Object.entries(MATERIALIZED_SPECS)) {
|
||||||
if (Array.isArray(plugin[field])) {
|
const materializedPath = spec.paths
|
||||||
const sortedEntries = sortPluginEntries(plugin[field]);
|
.map((subdir) => path.join(pluginPath, subdir))
|
||||||
if (!arraysEqual(plugin[field], sortedEntries)) {
|
.find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isDirectory());
|
||||||
plugin[field] = sortedEntries;
|
if (!materializedPath) {
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const materializedPath = path.join(pluginPath, spec.path);
|
|
||||||
if (!fs.existsSync(materializedPath) || !fs.statSync(materializedPath).isDirectory()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const restored = spec.restore(materializedPath);
|
const restored = spec.restore(materializedPath);
|
||||||
if (!arraysEqual(plugin[field], restored)) {
|
const composition = plugin.extensions?.[AWESOME_COPILOT_NAMESPACE];
|
||||||
plugin[field] = restored;
|
if (!arraysEqual(composition?.[field], restored)) {
|
||||||
|
plugin.extensions ??= {};
|
||||||
|
plugin.extensions[AWESOME_COPILOT_NAMESPACE] ??= {};
|
||||||
|
plugin.extensions[AWESOME_COPILOT_NAMESPACE][field] = restored;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,16 +119,23 @@ function cleanPlugin(pluginPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let removed = 0;
|
let removed = 0;
|
||||||
for (const { path: subdir } of Object.values(MATERIALIZED_SPECS)) {
|
for (const { paths } of Object.values(MATERIALIZED_SPECS)) {
|
||||||
const target = path.join(pluginPath, subdir);
|
for (const subdir of paths) {
|
||||||
if (fs.existsSync(target) && fs.statSync(target).isDirectory()) {
|
const target = path.join(pluginPath, subdir);
|
||||||
const count = countFiles(target);
|
if (fs.existsSync(target) && fs.statSync(target).isDirectory()) {
|
||||||
fs.rmSync(target, { recursive: true, force: true });
|
const count = countFiles(target);
|
||||||
removed += count;
|
fs.rmSync(target, { recursive: true, force: true });
|
||||||
console.log(` Removed ${path.basename(pluginPath)}/${subdir}/ (${count} files)`);
|
removed += count;
|
||||||
|
console.log(` Removed ${path.basename(pluginPath)}/${subdir}/ (${count} files)`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const copilotContentPath = path.join(pluginPath, COPILOT_CONTENT_DIR);
|
||||||
|
if (fs.existsSync(copilotContentPath) && fs.readdirSync(copilotContentPath).length === 0) {
|
||||||
|
fs.rmdirSync(copilotContentPath);
|
||||||
|
}
|
||||||
|
|
||||||
return { removed, manifestUpdated };
|
return { removed, manifestUpdated };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,6 +244,24 @@ function collectSkillDirectories(dir, rootDir = dir) {
|
|||||||
return skillDirs.sort();
|
return skillDirs.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collectDirectoriesContainingFile(dir, fileName, rootDir = dir) {
|
||||||
|
const directories = [];
|
||||||
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entryPath = path.join(dir, entry.name);
|
||||||
|
if (fs.existsSync(path.join(entryPath, fileName))) {
|
||||||
|
directories.push(toPosixPath(path.relative(rootDir, entryPath)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
directories.push(...collectDirectoriesContainingFile(entryPath, fileName, rootDir));
|
||||||
|
}
|
||||||
|
return directories.sort();
|
||||||
|
}
|
||||||
|
|
||||||
function arraysEqual(left, right) {
|
function arraysEqual(left, right) {
|
||||||
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
|
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
|
||||||
return false;
|
return false;
|
||||||
@@ -238,10 +270,6 @@ function arraysEqual(left, right) {
|
|||||||
return left.every((value, index) => value === right[index]);
|
return left.every((value, index) => value === right[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortPluginEntries(entries) {
|
|
||||||
return [...entries].sort((left, right) => left.localeCompare(right));
|
|
||||||
}
|
|
||||||
|
|
||||||
function toPosixPath(filePath) {
|
function toPosixPath(filePath) {
|
||||||
return filePath.split(path.sep).join("/");
|
return filePath.split(path.sep).join("/");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -618,10 +618,9 @@ function generatePluginsData(gitDates, resourceIndex = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build items list from spec fields (agents, commands, skills, mcpServers)
|
// Build items list from supported composition fields.
|
||||||
const items = [
|
const items = [
|
||||||
...agentItems,
|
...agentItems,
|
||||||
...(composition.commands || []).map((p) => ({ kind: "prompt", path: p })),
|
|
||||||
...(composition.skills || []).map((p) => ({ kind: "skill", path: p })),
|
...(composition.skills || []).map((p) => ({ kind: "skill", path: p })),
|
||||||
...extensionItems,
|
...extensionItems,
|
||||||
...mcpItems,
|
...mcpItems,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const PLUGINS_DIR = path.join(ROOT_FOLDER, "plugins");
|
|||||||
const EXTENSIONS_DIR = path.join(ROOT_FOLDER, "extensions");
|
const EXTENSIONS_DIR = path.join(ROOT_FOLDER, "extensions");
|
||||||
const COPILOT_NAMESPACE = "com.github.copilot";
|
const COPILOT_NAMESPACE = "com.github.copilot";
|
||||||
const AWESOME_COPILOT_NAMESPACE = "com.github.awesome-copilot";
|
const AWESOME_COPILOT_NAMESPACE = "com.github.awesome-copilot";
|
||||||
|
const COPILOT_CONTENT_DIR = COPILOT_NAMESPACE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively copy a directory.
|
* Recursively copy a directory.
|
||||||
@@ -49,9 +50,6 @@ function resolveSource(relPath) {
|
|||||||
if (relPath.startsWith("./hooks/")) {
|
if (relPath.startsWith("./hooks/")) {
|
||||||
return path.join(ROOT_FOLDER, "hooks", relPath.replace(/^\.\/hooks\//, ""));
|
return path.join(ROOT_FOLDER, "hooks", relPath.replace(/^\.\/hooks\//, ""));
|
||||||
}
|
}
|
||||||
if (relPath.startsWith("./commands/")) {
|
|
||||||
return path.join(ROOT_FOLDER, "commands", relPath.replace(/^\.\/commands\//, ""));
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +112,7 @@ export function materializePlugins() {
|
|||||||
const composition = metadata.extensions?.[AWESOME_COPILOT_NAMESPACE] ?? {};
|
const composition = metadata.extensions?.[AWESOME_COPILOT_NAMESPACE] ?? {};
|
||||||
|
|
||||||
// Process repository composition fields.
|
// Process repository composition fields.
|
||||||
for (const field of ["agents", "commands", "hooks", "skills"]) {
|
for (const field of ["agents", "hooks", "skills"]) {
|
||||||
const entries = composition[field];
|
const entries = composition[field];
|
||||||
if (!Array.isArray(entries)) continue;
|
if (!Array.isArray(entries)) continue;
|
||||||
for (const relPath of entries) {
|
for (const relPath of entries) {
|
||||||
@@ -129,7 +127,10 @@ export function materializePlugins() {
|
|||||||
warnings++;
|
warnings++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const dest = path.join(pluginPath, relPath.replace(/^\.\//, "").replace(/\/$/, ""));
|
const relativeDestination = relPath.replace(/^\.\//, "").replace(/\/$/, "");
|
||||||
|
const dest = field === "skills"
|
||||||
|
? path.join(pluginPath, relativeDestination)
|
||||||
|
: path.join(pluginPath, COPILOT_CONTENT_DIR, relativeDestination);
|
||||||
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||||
if (fs.statSync(src).isDirectory()) copyDirRecursive(src, dest);
|
if (fs.statSync(src).isDirectory()) copyDirRecursive(src, dest);
|
||||||
else fs.copyFileSync(src, dest);
|
else fs.copyFileSync(src, dest);
|
||||||
@@ -153,17 +154,16 @@ export function materializePlugins() {
|
|||||||
warnings++;
|
warnings++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Extensions are conventional plugin content and belong under the
|
const dest = path.join(pluginPath, COPILOT_CONTENT_DIR, "extensions", extensionName);
|
||||||
// plugin's top-level extensions directory, not the client namespace.
|
|
||||||
const dest = path.join(pluginPath, "extensions", extensionName);
|
|
||||||
copyDirRecursive(src, dest);
|
copyDirRecursive(src, dest);
|
||||||
totalExtensions++;
|
totalExtensions++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit a spec-compliant served manifest for the marketplace branch.
|
// Emit a spec-compliant served manifest for the marketplace branch.
|
||||||
// Source manifests keep composition fields (agents and skills)
|
// Source manifests keep repository composition fields for build tooling.
|
||||||
// for build tooling. The served manifest retains only Agent Plugins v1.0.0 fields
|
// The served manifest retains only Agent Plugins v1.0.0 fields; standard
|
||||||
// so the runtime uses conventional directory discovery for all content.
|
// skills are discovered from skills/, while Copilot-specific content is
|
||||||
|
// discovered from com.github.copilot/.
|
||||||
const SPEC_FIELDS = new Set(["$schema", "name", "version", "description", "author",
|
const SPEC_FIELDS = new Set(["$schema", "name", "version", "description", "author",
|
||||||
"homepage", "repository", "license", "keywords", "extensions"]);
|
"homepage", "repository", "license", "keywords", "extensions"]);
|
||||||
const AGENT_PLUGINS_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
|
const AGENT_PLUGINS_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
|
||||||
|
|||||||
@@ -800,7 +800,6 @@ function generatePluginsSection(pluginsDir) {
|
|||||||
: 0;
|
: 0;
|
||||||
const itemCount =
|
const itemCount =
|
||||||
(composition.agents || []).length +
|
(composition.agents || []).length +
|
||||||
(composition.commands || []).length +
|
|
||||||
(composition.skills || []).length +
|
(composition.skills || []).length +
|
||||||
extensionReferences +
|
extensionReferences +
|
||||||
implicitExtension;
|
implicitExtension;
|
||||||
@@ -869,7 +868,6 @@ function generateFeaturedPluginsSection(pluginsDir) {
|
|||||||
: 0;
|
: 0;
|
||||||
const itemCount =
|
const itemCount =
|
||||||
(composition.agents || []).length +
|
(composition.agents || []).length +
|
||||||
(composition.commands || []).length +
|
|
||||||
(composition.skills || []).length +
|
(composition.skills || []).length +
|
||||||
extensionReferences +
|
extensionReferences +
|
||||||
implicitExtension;
|
implicitExtension;
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ function validateSpecPaths(plugin) {
|
|||||||
const errors = [];
|
const errors = [];
|
||||||
const specs = {
|
const specs = {
|
||||||
agents: { prefix: "./agents/", suffix: ".md", repoDir: "agents", repoSuffix: ".agent.md" },
|
agents: { prefix: "./agents/", suffix: ".md", repoDir: "agents", repoSuffix: ".agent.md" },
|
||||||
commands: { prefix: "./commands/", suffix: ".md", repoDir: "commands", repoSuffix: ".md" },
|
|
||||||
hooks: { prefix: "./hooks/", suffix: "/", repoDir: "hooks", repoFile: "README.md" },
|
hooks: { prefix: "./hooks/", suffix: "/", repoDir: "hooks", repoFile: "README.md" },
|
||||||
skills: { prefix: "./skills/", suffix: "/", repoDir: "skills", repoFile: "SKILL.md" },
|
skills: { prefix: "./skills/", suffix: "/", repoDir: "skills", repoFile: "SKILL.md" },
|
||||||
};
|
};
|
||||||
@@ -215,7 +214,7 @@ function validateExtensionReferences(plugin, pluginDir) {
|
|||||||
|
|
||||||
function validateCompositionNamespace(plugin) {
|
function validateCompositionNamespace(plugin) {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
const compositionFields = ["agents", "commands", "hooks", "mcpServers", "skills"];
|
const compositionFields = ["agents", "hooks", "mcpServers", "skills"];
|
||||||
const extensions = plugin.extensions;
|
const extensions = plugin.extensions;
|
||||||
const composition = extensions?.[AWESOME_COPILOT_NAMESPACE];
|
const composition = extensions?.[AWESOME_COPILOT_NAMESPACE];
|
||||||
|
|
||||||
@@ -291,7 +290,7 @@ function validatePlugin(folderName) {
|
|||||||
|
|
||||||
// Rule 5b: license (shared with external plugins). Non-SPDX is a warning, not an error.
|
// Rule 5b: license (shared with external plugins). Non-SPDX is a warning, not an error.
|
||||||
const warnings = [];
|
const warnings = [];
|
||||||
for (const field of ["agents", "commands", "hooks", "mcpServers", "skills"]) {
|
for (const field of ["agents", "hooks", "mcpServers", "skills"]) {
|
||||||
if (plugin[field] !== undefined) {
|
if (plugin[field] !== undefined) {
|
||||||
errors.push(`${field} must be moved to extensions["${AWESOME_COPILOT_NAMESPACE}"].${field}`);
|
errors.push(`${field} must be moved to extensions["${AWESOME_COPILOT_NAMESPACE}"].${field}`);
|
||||||
}
|
}
|
||||||
@@ -301,7 +300,7 @@ function validatePlugin(folderName) {
|
|||||||
errors.push(...licenseResult.errors);
|
errors.push(...licenseResult.errors);
|
||||||
warnings.push(...licenseResult.warnings);
|
warnings.push(...licenseResult.warnings);
|
||||||
|
|
||||||
// Rule 6: agents, commands, skills paths
|
// Rule 6: agents, hooks, and skills paths
|
||||||
const specErrors = validateSpecPaths(plugin);
|
const specErrors = validateSpecPaths(plugin);
|
||||||
errors.push(...specErrors);
|
errors.push(...specErrors);
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 380 KiB After Width: | Height: | Size: 380 KiB |
|
Before Width: | Height: | Size: 296 KiB After Width: | Height: | Size: 296 KiB |
|
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 255 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 538 KiB After Width: | Height: | Size: 538 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
|
Before Width: | Height: | Size: 937 KiB After Width: | Height: | Size: 937 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 518 B After Width: | Height: | Size: 518 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 246 B After Width: | Height: | Size: 246 B |
|
Before Width: | Height: | Size: 175 B After Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 191 B |
|
Before Width: | Height: | Size: 562 B After Width: | Height: | Size: 562 B |
|
Before Width: | Height: | Size: 244 B After Width: | Height: | Size: 244 B |
|
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 235 B |
|
Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 434 B |
|
Before Width: | Height: | Size: 408 B After Width: | Height: | Size: 408 B |
|
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 438 B |
|
Before Width: | Height: | Size: 233 B After Width: | Height: | Size: 233 B |
|
Before Width: | Height: | Size: 243 B After Width: | Height: | Size: 243 B |
|
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 306 B |
|
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 234 B |
|
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B |
|
Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 258 B |
|
Before Width: | Height: | Size: 167 B After Width: | Height: | Size: 167 B |
|
Before Width: | Height: | Size: 660 B After Width: | Height: | Size: 660 B |
|
Before Width: | Height: | Size: 240 B After Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 195 B After Width: | Height: | Size: 195 B |