mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-23 03:45:13 +00:00
Convert all prompts to skills and update plugin manifests
Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com>
This commit is contained in:
136
eng/migrate-prompts-to-skills.mjs
Executable file
136
eng/migrate-prompts-to-skills.mjs
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { ROOT_FOLDER, PROMPTS_DIR, SKILLS_DIR } from "./constants.mjs";
|
||||
import { parseFrontmatter } from "./yaml-parser.mjs";
|
||||
|
||||
/**
|
||||
* Convert a prompt file to a skill folder
|
||||
* @param {string} promptFilePath - Full path to the prompt file
|
||||
* @returns {object} Result with success status and details
|
||||
*/
|
||||
function convertPromptToSkill(promptFilePath) {
|
||||
const filename = path.basename(promptFilePath);
|
||||
const baseName = filename.replace(".prompt.md", "");
|
||||
|
||||
console.log(`\nConverting: ${baseName}`);
|
||||
|
||||
// Parse the prompt file frontmatter
|
||||
const frontmatter = parseFrontmatter(promptFilePath);
|
||||
const content = fs.readFileSync(promptFilePath, "utf8");
|
||||
|
||||
// Extract the content after frontmatter
|
||||
const frontmatterEndMatch = content.match(/^---\n[\s\S]*?\n---\n/);
|
||||
const mainContent = frontmatterEndMatch
|
||||
? content.substring(frontmatterEndMatch[0].length).trim()
|
||||
: content.trim();
|
||||
|
||||
// Create skill folder
|
||||
const skillFolderPath = path.join(SKILLS_DIR, baseName);
|
||||
if (fs.existsSync(skillFolderPath)) {
|
||||
console.log(` ⚠️ Skill folder already exists: ${baseName}`);
|
||||
return { success: false, reason: "already-exists", name: baseName };
|
||||
}
|
||||
|
||||
fs.mkdirSync(skillFolderPath, { recursive: true });
|
||||
|
||||
// Build new frontmatter for SKILL.md
|
||||
const skillFrontmatter = {
|
||||
name: baseName,
|
||||
description: frontmatter?.description || `Skill converted from ${filename}`,
|
||||
};
|
||||
|
||||
// Build SKILL.md content
|
||||
const skillContent = `---
|
||||
name: ${skillFrontmatter.name}
|
||||
description: '${skillFrontmatter.description.replace(/'/g, "'\\''")}'
|
||||
---
|
||||
|
||||
${mainContent}
|
||||
`;
|
||||
|
||||
// Write SKILL.md
|
||||
const skillFilePath = path.join(skillFolderPath, "SKILL.md");
|
||||
fs.writeFileSync(skillFilePath, skillContent, "utf8");
|
||||
|
||||
console.log(` ✓ Created skill: ${baseName}`);
|
||||
return { success: true, name: baseName, path: skillFolderPath };
|
||||
}
|
||||
|
||||
/**
|
||||
* Main migration function
|
||||
*/
|
||||
function main() {
|
||||
console.log("=".repeat(60));
|
||||
console.log("Starting Prompt to Skills Migration");
|
||||
console.log("=".repeat(60));
|
||||
|
||||
// Check if prompts directory exists
|
||||
if (!fs.existsSync(PROMPTS_DIR)) {
|
||||
console.error(`Error: Prompts directory not found: ${PROMPTS_DIR}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Get all prompt files
|
||||
const promptFiles = fs
|
||||
.readdirSync(PROMPTS_DIR)
|
||||
.filter((file) => file.endsWith(".prompt.md"))
|
||||
.map((file) => path.join(PROMPTS_DIR, file));
|
||||
|
||||
console.log(`Found ${promptFiles.length} prompt files to convert\n`);
|
||||
|
||||
const results = {
|
||||
success: [],
|
||||
alreadyExists: [],
|
||||
failed: [],
|
||||
};
|
||||
|
||||
// Convert each prompt
|
||||
for (const promptFile of promptFiles) {
|
||||
try {
|
||||
const result = convertPromptToSkill(promptFile);
|
||||
if (result.success) {
|
||||
results.success.push(result.name);
|
||||
} else if (result.reason === "already-exists") {
|
||||
results.alreadyExists.push(result.name);
|
||||
} else {
|
||||
results.failed.push(result.name);
|
||||
}
|
||||
} catch (error) {
|
||||
const baseName = path.basename(promptFile, ".prompt.md");
|
||||
console.error(` ✗ Error converting ${baseName}: ${error.message}`);
|
||||
results.failed.push(baseName);
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary
|
||||
console.log("\n" + "=".repeat(60));
|
||||
console.log("Migration Summary");
|
||||
console.log("=".repeat(60));
|
||||
console.log(`✓ Successfully converted: ${results.success.length}`);
|
||||
console.log(`⚠ Already existed: ${results.alreadyExists.length}`);
|
||||
console.log(`✗ Failed: ${results.failed.length}`);
|
||||
console.log(`Total processed: ${promptFiles.length}`);
|
||||
|
||||
if (results.failed.length > 0) {
|
||||
console.log("\nFailed conversions:");
|
||||
results.failed.forEach((name) => console.log(` - ${name}`));
|
||||
}
|
||||
|
||||
if (results.alreadyExists.length > 0) {
|
||||
console.log("\nSkipped (already exist):");
|
||||
results.alreadyExists.forEach((name) => console.log(` - ${name}`));
|
||||
}
|
||||
|
||||
console.log("\n✅ Migration complete!");
|
||||
console.log(
|
||||
"\nNext steps:\n" +
|
||||
"1. Run 'npm run skill:validate' to validate all new skills\n" +
|
||||
"2. Update plugin manifests to reference skills instead of commands\n" +
|
||||
"3. Remove prompts directory after testing\n"
|
||||
);
|
||||
}
|
||||
|
||||
// Run migration
|
||||
main();
|
||||
152
eng/update-plugin-commands-to-skills.mjs
Executable file
152
eng/update-plugin-commands-to-skills.mjs
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { PLUGINS_DIR } from "./constants.mjs";
|
||||
|
||||
/**
|
||||
* Convert commands references to skills references in a plugin.json
|
||||
* @param {string} pluginJsonPath - Path to the plugin.json file
|
||||
* @returns {object} Result with success status and details
|
||||
*/
|
||||
function updatePluginManifest(pluginJsonPath) {
|
||||
const pluginDir = path.dirname(path.dirname(path.dirname(pluginJsonPath)));
|
||||
const pluginName = path.basename(pluginDir);
|
||||
|
||||
console.log(`\nProcessing plugin: ${pluginName}`);
|
||||
|
||||
// Read and parse plugin.json
|
||||
let plugin;
|
||||
try {
|
||||
const content = fs.readFileSync(pluginJsonPath, "utf8");
|
||||
plugin = JSON.parse(content);
|
||||
} catch (error) {
|
||||
console.log(` ✗ Error reading/parsing: ${error.message}`);
|
||||
return { success: false, name: pluginName, reason: "parse-error" };
|
||||
}
|
||||
|
||||
// Check if plugin has commands field
|
||||
if (!plugin.commands || !Array.isArray(plugin.commands)) {
|
||||
console.log(` ℹ No commands field found`);
|
||||
return { success: false, name: pluginName, reason: "no-commands" };
|
||||
}
|
||||
|
||||
const commandCount = plugin.commands.length;
|
||||
console.log(` Found ${commandCount} command(s) to convert`);
|
||||
|
||||
// Convert commands to skills format
|
||||
// Commands: "./commands/foo.md" → Skills: "./skills/foo/"
|
||||
const skills = plugin.commands.map((cmd) => {
|
||||
const basename = path.basename(cmd, ".md");
|
||||
return `./skills/${basename}/`;
|
||||
});
|
||||
|
||||
// Initialize skills array if it doesn't exist
|
||||
if (!plugin.skills) {
|
||||
plugin.skills = [];
|
||||
}
|
||||
|
||||
// Add converted commands to skills array
|
||||
plugin.skills.push(...skills);
|
||||
|
||||
// Remove commands field
|
||||
delete plugin.commands;
|
||||
|
||||
// Write updated plugin.json
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
pluginJsonPath,
|
||||
JSON.stringify(plugin, null, 2) + "\n",
|
||||
"utf8"
|
||||
);
|
||||
console.log(` ✓ Converted ${commandCount} command(s) to skills`);
|
||||
return { success: true, name: pluginName, count: commandCount };
|
||||
} catch (error) {
|
||||
console.log(` ✗ Error writing file: ${error.message}`);
|
||||
return { success: false, name: pluginName, reason: "write-error" };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to update all plugin manifests
|
||||
*/
|
||||
function main() {
|
||||
console.log("=".repeat(60));
|
||||
console.log("Updating Plugin Manifests: Commands → Skills");
|
||||
console.log("=".repeat(60));
|
||||
|
||||
// Check if plugins directory exists
|
||||
if (!fs.existsSync(PLUGINS_DIR)) {
|
||||
console.error(`Error: Plugins directory not found: ${PLUGINS_DIR}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Find all plugin.json files
|
||||
const pluginDirs = fs
|
||||
.readdirSync(PLUGINS_DIR, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => entry.name);
|
||||
|
||||
console.log(`Found ${pluginDirs.length} plugin directory(ies)\n`);
|
||||
|
||||
const results = {
|
||||
updated: [],
|
||||
noCommands: [],
|
||||
failed: [],
|
||||
};
|
||||
|
||||
// Process each plugin
|
||||
for (const dirName of pluginDirs) {
|
||||
const pluginJsonPath = path.join(
|
||||
PLUGINS_DIR,
|
||||
dirName,
|
||||
".github/plugin",
|
||||
"plugin.json"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(pluginJsonPath)) {
|
||||
console.log(`\nSkipping ${dirName}: no plugin.json found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const result = updatePluginManifest(pluginJsonPath);
|
||||
if (result.success) {
|
||||
results.updated.push({ name: result.name, count: result.count });
|
||||
} else if (result.reason === "no-commands") {
|
||||
results.noCommands.push(result.name);
|
||||
} else {
|
||||
results.failed.push(result.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary
|
||||
console.log("\n" + "=".repeat(60));
|
||||
console.log("Update Summary");
|
||||
console.log("=".repeat(60));
|
||||
console.log(`✓ Updated plugins: ${results.updated.length}`);
|
||||
console.log(`ℹ No commands field: ${results.noCommands.length}`);
|
||||
console.log(`✗ Failed: ${results.failed.length}`);
|
||||
console.log(`Total processed: ${pluginDirs.length}`);
|
||||
|
||||
if (results.updated.length > 0) {
|
||||
console.log("\nUpdated plugins:");
|
||||
results.updated.forEach(({ name, count }) =>
|
||||
console.log(` - ${name} (${count} command(s) → skills)`)
|
||||
);
|
||||
}
|
||||
|
||||
if (results.failed.length > 0) {
|
||||
console.log("\nFailed updates:");
|
||||
results.failed.forEach((name) => console.log(` - ${name}`));
|
||||
}
|
||||
|
||||
console.log("\n✅ Plugin manifest updates complete!");
|
||||
console.log(
|
||||
"\nNext steps:\n" +
|
||||
"1. Run 'npm run plugin:validate' to validate all updated plugins\n" +
|
||||
"2. Test that plugins work correctly\n"
|
||||
);
|
||||
}
|
||||
|
||||
// Run the update
|
||||
main();
|
||||
Reference in New Issue
Block a user