diff --git a/AGENTS.md b/AGENTS.md index b2dbd6fd..faed7127 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,6 +9,7 @@ The Awesome GitHub Copilot repository is a community-driven collection of custom - **Instructions** - Coding standards and best practices applied to specific file patterns - **Skills** - Self-contained folders with instructions and bundled resources for specialized tasks - **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 - **Plugins** - Installable packages that group related agents, commands, and skills around specific themes ## Repository Structure @@ -20,6 +21,7 @@ The Awesome GitHub Copilot repository is a community-driven collection of custom ├── instructions/ # Coding standards and guidelines (.instructions.md files) ├── skills/ # Agent Skills folders (each with SKILL.md and optional bundled assets) ├── hooks/ # Automated workflow hooks (folders with README.md + hooks.json) +├── workflows/ # Agentic Workflows (folders with README.md + workflow .md files) ├── plugins/ # Installable plugin packages (folders with plugin.json) ├── docs/ # Documentation for different resource types ├── eng/ # Build and automation scripts @@ -96,6 +98,17 @@ All agent files (`*.agent.md`), prompt files (`*.prompt.md`), and instruction fi - Follow the [GitHub Copilot hooks specification](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/use-hooks) - Optionally includes `tags` field for categorization +#### Workflow Folders (workflows/*/README.md) +- Each workflow is a folder containing a `README.md` file with frontmatter and one or more `.md` workflow files +- README.md must have `name` field (human-readable name) +- README.md must have `description` field (wrapped in single quotes, not empty) +- README.md should have `triggers` field (array of trigger types, e.g., `['schedule', 'issues']`) +- Workflow `.md` files contain YAML frontmatter (`on`, `permissions`, `safe-outputs`) and natural language instructions +- Folder names should be lower case with words separated by hyphens +- Can include bundled assets (scripts, configuration files) +- Optionally includes `tags` field for categorization +- Follow the [GitHub Agentic Workflows specification](https://github.github.com/gh-aw) + #### Plugin Folders (plugins/*) - Each plugin is a folder containing a `.github/plugin/plugin.json` file with metadata - plugin.json must have `name` field (matching the folder name) @@ -107,7 +120,7 @@ All agent files (`*.agent.md`), prompt files (`*.prompt.md`), and instruction fi ### Adding New Resources -When adding a new agent, prompt, instruction, skill, hook, or plugin: +When adding a new agent, prompt, instruction, skill, hook, workflow, or plugin: **For Agents, Prompts, and Instructions:** 1. Create the file with proper front matter @@ -125,6 +138,15 @@ When adding a new agent, prompt, instruction, skill, hook, or plugin: 7. Verify the hook appears in the generated README +**For Workflows:** +1. Create a new folder in `workflows/` with a descriptive name +2. Create `README.md` with proper frontmatter (name, description, triggers, tags) +3. Add one or more `.md` workflow files with `on`, `permissions`, and `safe-outputs` frontmatter +4. Add any bundled scripts or assets to the folder +5. Update the README.md by running: `npm run build` +6. Verify the workflow appears in the generated README + + **For Skills:** 1. Run `npm run skill:create` to scaffold a new skill folder 2. Edit the generated SKILL.md file with your instructions @@ -241,6 +263,17 @@ For hook folders (hooks/*/): - [ ] Follows [GitHub Copilot hooks specification](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/use-hooks) - [ ] Optionally includes `tags` array field for categorization +For workflow folders (workflows/*/): +- [ ] Folder contains a README.md file with markdown front matter +- [ ] Has `name` field with human-readable name +- [ ] Has non-empty `description` field wrapped in single quotes +- [ ] Has `triggers` array field listing workflow trigger types +- [ ] Folder name is lower case with hyphens +- [ ] Contains at least one `.md` workflow file with `on` and `permissions` in frontmatter +- [ ] Workflow uses least-privilege permissions and safe outputs +- [ ] Follows [GitHub Agentic Workflows specification](https://github.github.com/gh-aw) +- [ ] Optionally includes `tags` array field for categorization + For plugins (plugins/*/): - [ ] Directory contains a `.github/plugin/plugin.json` file - [ ] Directory contains a `README.md` file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index da0d4e91..da0b2d5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -161,11 +161,75 @@ plugins/my-plugin-id/ - **Clear purpose**: The plugin should solve a specific problem or workflow - **Validate before submitting**: Run `npm run plugin:validate` to ensure your plugin is valid +### Adding Agentic Workflows + +[Agentic Workflows](https://github.github.com/gh-aw) are AI-powered repository automations that run coding agents in GitHub Actions. Defined in markdown with natural language instructions, they enable scheduled and event-triggered automation with built-in guardrails. + +1. **Create a new workflow folder**: Add a new folder in the `workflows/` directory with a descriptive name (e.g., `daily-issues-report`) +2. **Create a `README.md`**: Add a `README.md` with frontmatter containing `name`, `description`, `triggers`, and optionally `tags` +3. **Add workflow files**: Include one or more `.md` workflow files with YAML frontmatter (`on`, `permissions`, `safe-outputs`) and natural language instructions +4. **Add optional assets**: Include any helper scripts or configuration files referenced by the workflow +5. **Update the README**: Run `npm run build` to update the generated README tables + +#### Workflow folder structure + +``` +workflows/daily-issues-report/ +├── README.md # Workflow documentation with frontmatter +└── daily-issues-report.md # Agentic workflow file +``` + +#### README.md frontmatter example + +```markdown +--- +name: 'Daily Issues Report' +description: 'Generates a daily summary of open issues and recent activity as a GitHub issue' +triggers: ['schedule'] +tags: ['reporting', 'issues', 'automation'] +--- +``` + +#### Workflow file example + +```markdown +--- +on: + schedule: daily on weekdays +permissions: + contents: read + issues: read +safe-outputs: + create-issue: + title-prefix: "[daily-report] " + labels: [report] +--- + +## Daily Issues Report + +Create a daily summary of open issues for the team. + +## What to Include + +- New issues opened in the last 24 hours +- Issues closed or resolved +- Stale issues that need attention +``` + +#### Workflow Guidelines + +- **Security first**: Use least-privilege permissions and safe outputs instead of direct write access +- **Clear instructions**: Write clear natural language instructions in the workflow body +- **Descriptive names**: Use lowercase folder names with hyphens +- **Test locally**: Use `gh aw run` to test workflows before contributing +- **Documentation**: Include a thorough README explaining what the workflow does and how to use it +- Learn more at the [Agentic Workflows documentation](https://github.github.com/gh-aw) + ## Submitting Your Contribution 1. **Fork this repository** 2. **Create a new branch** for your contribution -3. **Add your instruction, prompt file, chatmode, or plugin** following the guidelines above +3. **Add your instruction, prompt file, chatmode, workflow, or plugin** following the guidelines above 4. **Run the update script**: `npm start` to update the README with your new file (make sure you run `npm install` first if you haven't already) - A GitHub Actions workflow will verify that this step was performed correctly - If the README.md would be modified by running the script, the PR check will fail with a comment showing the required changes @@ -234,6 +298,7 @@ We welcome many kinds of contributions, including the custom categories below: | **Prompts** | Reusable or one-off prompts for GitHub Copilot | ⌨️ | | **Agents** | Defined GitHub Copilot roles or personalities | 🎭 | | **Skills** | Specialized knowledge of a task for GitHub Copilot | 🧰 | +| **Workflows** | Agentic Workflows for AI-powered repository automation | ⚡ | | **Plugins** | Installable packages of related prompts, agents, or skills | 🎁 | In addition, all standard contribution types supported by [All Contributors](https://allcontributors.org/emoji-key/) are recognized. diff --git a/README.md b/README.md index 82e62f64..59578360 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This repository provides a comprehensive toolkit for enhancing GitHub Copilot wi - **👉 [Awesome Prompts](docs/README.prompts.md)** - Focused, task-specific prompts for generating code, documentation, and solving specific problems - **👉 [Awesome Instructions](docs/README.instructions.md)** - Comprehensive coding standards and best practices that apply to specific file patterns or entire projects - **👉 [Awesome Hooks](docs/README.hooks.md)** - Automated workflows triggered by specific events during development, testing, and deployment +- **👉 [Awesome Agentic Workflows](docs/README.workflows.md)** - AI-powered repository automations that run coding agents in GitHub Actions with natural language instructions - **👉 [Awesome Skills](docs/README.skills.md)** - Self-contained folders with instructions and bundled resources that enhance AI capabilities for specialized tasks - **👉 [Awesome Plugins](docs/README.plugins.md)** - Curated plugins of related prompts, agents, and skills organized around specific themes and workflows - **👉 [Awesome Cookbook Recipes](cookbook/README.md)** - Practical, copy-paste-ready code snippets and real-world examples for working with GitHub Copilot tools and features @@ -101,6 +102,10 @@ Instructions automatically apply to files based on their patterns and provide co Hooks enable automated workflows triggered by specific events during GitHub Copilot coding agent sessions (like sessionStart, sessionEnd, userPromptSubmitted). They can automate tasks like logging, auto-committing changes, or integrating with external services. +### ⚡ Agentic Workflows + +[Agentic Workflows](https://github.github.com/gh-aw) are AI-powered repository automations that run coding agents in GitHub Actions. Defined in markdown with natural language instructions, they enable event-triggered and scheduled automation — from issue triage to daily reports. + ## 🎯 Why Use Awesome GitHub Copilot? - **Productivity**: Pre-built agents, prompts and instructions save time and provide consistent results. @@ -112,7 +117,7 @@ Hooks enable automated workflows triggered by specific events during GitHub Copi We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details on how to: -- Add new prompts, instructions, hooks, agents, or skills +- Add new prompts, instructions, hooks, workflows, agents, or skills - Improve existing content - Report issues or suggest enhancements @@ -131,6 +136,8 @@ For AI coding agents working with this project, refer to [AGENTS.md](AGENTS.md) ├── prompts/ # Task-specific prompts (.prompt.md) ├── instructions/ # Coding standards and best practices (.instructions.md) ├── agents/ # AI personas and specialized modes (.agent.md) +├── hooks/ # Automated hooks for Copilot coding agent sessions +├── workflows/ # Agentic Workflows for GitHub Actions automation ├── plugins/ # Installable plugins bundling related items ├── scripts/ # Utility scripts for maintenance └── skills/ # AI capabilities for specialized tasks @@ -152,7 +159,7 @@ The customizations in this repository are sourced from and created by third-part --- -**Ready to supercharge your coding experience?** Start exploring our [prompts](docs/README.prompts.md), [instructions](docs/README.instructions.md), [hooks](docs/README.hooks.md), and [custom agents](docs/README.agents.md)! +**Ready to supercharge your coding experience?** Start exploring our [prompts](docs/README.prompts.md), [instructions](docs/README.instructions.md), [hooks](docs/README.hooks.md), [agentic workflows](docs/README.workflows.md), and [custom agents](docs/README.agents.md)! ## Contributors ✨ diff --git a/docs/README.workflows.md b/docs/README.workflows.md new file mode 100644 index 00000000..026b7058 --- /dev/null +++ b/docs/README.workflows.md @@ -0,0 +1,31 @@ +# ⚡ Agentic Workflows + +[Agentic Workflows](https://github.github.com/gh-aw) are AI-powered repository automations that run coding agents in GitHub Actions. Defined in markdown with natural language instructions, they enable event-triggered and scheduled automation with built-in guardrails and security-first design. + +### How to Use Agentic Workflows + +**What's Included:** +- Each workflow is a folder containing a `README.md` and one or more `.md` workflow files +- Workflows are compiled to `.lock.yml` GitHub Actions files via `gh aw compile` +- Workflows follow the [GitHub Agentic Workflows specification](https://github.github.com/gh-aw) + +**To Install:** +- Install the `gh aw` CLI extension: `gh extension install github/gh-aw` +- Copy the workflow `.md` file to your repository's `.github/workflows/` directory +- Compile with `gh aw compile` to generate the `.lock.yml` file +- Commit both the `.md` and `.lock.yml` files + +**To Activate/Use:** +- Workflows run automatically based on their configured triggers (schedules, events, slash commands) +- Use `gh aw run ` to trigger a manual run +- Monitor runs with `gh aw status` and `gh aw logs` + +**When to Use:** +- Automate issue triage and labeling +- Generate daily status reports +- Maintain documentation automatically +- Run scheduled code quality checks +- Respond to slash commands in issues and PRs +- Orchestrate multi-step repository automation + +_No entries found yet._ \ No newline at end of file diff --git a/eng/constants.mjs b/eng/constants.mjs index 1f1e95ec..bf5a8904 100644 --- a/eng/constants.mjs +++ b/eng/constants.mjs @@ -127,6 +127,36 @@ Hooks enable automated workflows triggered by specific events during GitHub Copi - Track usage analytics - Integrate with external tools and services - Custom session workflows`, + + workflowsSection: `## ⚡ Agentic Workflows + +[Agentic Workflows](https://github.github.com/gh-aw) are AI-powered repository automations that run coding agents in GitHub Actions. Defined in markdown with natural language instructions, they enable event-triggered and scheduled automation with built-in guardrails and security-first design.`, + + workflowsUsage: `### How to Use Agentic Workflows + +**What's Included:** +- Each workflow is a folder containing a \`README.md\` and one or more \`.md\` workflow files +- Workflows are compiled to \`.lock.yml\` GitHub Actions files via \`gh aw compile\` +- Workflows follow the [GitHub Agentic Workflows specification](https://github.github.com/gh-aw) + +**To Install:** +- Install the \`gh aw\` CLI extension: \`gh extension install github/gh-aw\` +- Copy the workflow \`.md\` file to your repository's \`.github/workflows/\` directory +- Compile with \`gh aw compile\` to generate the \`.lock.yml\` file +- Commit both the \`.md\` and \`.lock.yml\` files + +**To Activate/Use:** +- Workflows run automatically based on their configured triggers (schedules, events, slash commands) +- Use \`gh aw run \` to trigger a manual run +- Monitor runs with \`gh aw status\` and \`gh aw logs\` + +**When to Use:** +- Automate issue triage and labeling +- Generate daily status reports +- Maintain documentation automatically +- Run scheduled code quality checks +- Respond to slash commands in issues and PRs +- Orchestrate multi-step repository automation`, }; const vscodeInstallImage = @@ -152,6 +182,7 @@ const AGENTS_DIR = path.join(ROOT_FOLDER, "agents"); const SKILLS_DIR = path.join(ROOT_FOLDER, "skills"); const HOOKS_DIR = path.join(ROOT_FOLDER, "hooks"); const PLUGINS_DIR = path.join(ROOT_FOLDER, "plugins"); +const WORKFLOWS_DIR = path.join(ROOT_FOLDER, "workflows"); const COOKBOOK_DIR = path.join(ROOT_FOLDER, "cookbook"); const MAX_PLUGIN_ITEMS = 50; @@ -182,6 +213,7 @@ export { SKILLS_DIR, TEMPLATES, vscodeInsidersInstallImage, - vscodeInstallImage + vscodeInstallImage, + WORKFLOWS_DIR }; diff --git a/eng/generate-website-data.mjs b/eng/generate-website-data.mjs index 5ac93e31..28b20fad 100644 --- a/eng/generate-website-data.mjs +++ b/eng/generate-website-data.mjs @@ -17,13 +17,15 @@ import { PLUGINS_DIR, PROMPTS_DIR, ROOT_FOLDER, - SKILLS_DIR + SKILLS_DIR, + WORKFLOWS_DIR } from "./constants.mjs"; import { getGitFileDates } from "./utils/git-dates.mjs"; import { parseFrontmatter, parseSkillMetadata, parseHookMetadata, + parseWorkflowMetadata, parseYamlFile, } from "./yaml-parser.mjs"; @@ -192,6 +194,67 @@ function generateHooksData(gitDates) { }; } +/** + * Generate workflows metadata (folder-based, similar to hooks) + */ +function generateWorkflowsData(gitDates) { + const workflows = []; + + if (!fs.existsSync(WORKFLOWS_DIR)) { + return { + items: workflows, + filters: { + triggers: [], + tags: [], + }, + }; + } + + const workflowFolders = fs.readdirSync(WORKFLOWS_DIR).filter((file) => { + const filePath = path.join(WORKFLOWS_DIR, file); + return fs.statSync(filePath).isDirectory(); + }); + + const allTriggers = new Set(); + const allTags = new Set(); + + for (const folder of workflowFolders) { + const workflowPath = path.join(WORKFLOWS_DIR, folder); + const metadata = parseWorkflowMetadata(workflowPath); + if (!metadata) continue; + + const relativePath = path + .relative(ROOT_FOLDER, workflowPath) + .replace(/\\/g, "/"); + const readmeRelativePath = `${relativePath}/README.md`; + + (metadata.triggers || []).forEach((t) => allTriggers.add(t)); + (metadata.tags || []).forEach((t) => allTags.add(t)); + + workflows.push({ + id: folder, + title: metadata.name, + description: metadata.description, + triggers: metadata.triggers || [], + tags: metadata.tags || [], + assets: metadata.assets || [], + path: relativePath, + readmeFile: readmeRelativePath, + lastUpdated: gitDates.get(readmeRelativePath) || null, + }); + } + + const sortedWorkflows = workflows.sort((a, b) => a.title.localeCompare(b.title)); + + return { + items: sortedWorkflows, + filters: { + triggers: Array.from(allTriggers).sort(), + tags: Array.from(allTags).sort(), + }, + }; +} + /** * Generate prompts metadata */ @@ -606,6 +669,7 @@ function generateSearchIndex( prompts, instructions, hooks, + workflows, skills, plugins ) { @@ -665,6 +729,20 @@ function generateSearchIndex( }); } + for (const workflow of workflows) { + index.push({ + type: "workflow", + id: workflow.id, + title: workflow.title, + description: workflow.description, + path: workflow.readmeFile, + lastUpdated: workflow.lastUpdated, + searchText: `${workflow.title} ${workflow.description} ${workflow.triggers.join( + " " + )} ${workflow.tags.join(" ")}`.toLowerCase(), + }); + } + for (const skill of skills) { index.push({ type: "skill", @@ -799,7 +877,7 @@ async function main() { // Load git dates for all resource files (single efficient git command) console.log("Loading git history for last updated dates..."); const gitDates = getGitFileDates( - ["agents/", "prompts/", "instructions/", "hooks/", "skills/", "plugins/"], + ["agents/", "prompts/", "instructions/", "hooks/", "workflows/", "skills/", "plugins/"], ROOT_FOLDER ); console.log(`✓ Loaded dates for ${gitDates.size} files\n`); @@ -817,6 +895,12 @@ async function main() { `✓ Generated ${hooks.length} hooks (${hooksData.filters.hooks.length} hook types, ${hooksData.filters.tags.length} tags)` ); + const workflowsData = generateWorkflowsData(gitDates); + const workflows = workflowsData.items; + console.log( + `✓ Generated ${workflows.length} workflows (${workflowsData.filters.triggers.length} triggers, ${workflowsData.filters.tags.length} tags)` + ); + const promptsData = generatePromptsData(gitDates); const prompts = promptsData.items; console.log( @@ -857,6 +941,7 @@ async function main() { prompts, instructions, hooks, + workflows, skills, plugins ); @@ -873,6 +958,11 @@ async function main() { JSON.stringify(hooksData, null, 2) ); + fs.writeFileSync( + path.join(WEBSITE_DATA_DIR, "workflows.json"), + JSON.stringify(workflowsData, null, 2) + ); + fs.writeFileSync( path.join(WEBSITE_DATA_DIR, "prompts.json"), JSON.stringify(promptsData, null, 2) @@ -917,6 +1007,7 @@ async function main() { instructions: instructions.length, skills: skills.length, hooks: hooks.length, + workflows: workflows.length, plugins: plugins.length, tools: tools.length, samples: samplesData.totalRecipes, diff --git a/eng/update-readme.mjs b/eng/update-readme.mjs index f14a0bc0..534a591a 100644 --- a/eng/update-readme.mjs +++ b/eng/update-readme.mjs @@ -17,12 +17,14 @@ import { TEMPLATES, vscodeInsidersInstallImage, vscodeInstallImage, + WORKFLOWS_DIR, } from "./constants.mjs"; import { extractMcpServerConfigs, parseFrontmatter, parseSkillMetadata, parseHookMetadata, + parseWorkflowMetadata, } from "./yaml-parser.mjs"; const __filename = fileURLToPath(import.meta.url); @@ -577,6 +579,67 @@ function generateHooksSection(hooksDir) { return `${TEMPLATES.hooksSection}\n${TEMPLATES.hooksUsage}\n\n${content}`; } +/** + * Generate the workflows section with a table of all agentic workflows + */ +function generateWorkflowsSection(workflowsDir) { + if (!fs.existsSync(workflowsDir)) { + console.log(`Workflows directory does not exist: ${workflowsDir}`); + return ""; + } + + // Get all workflow folders (directories) + const workflowFolders = fs.readdirSync(workflowsDir).filter((file) => { + const filePath = path.join(workflowsDir, file); + return fs.statSync(filePath).isDirectory(); + }); + + // Parse each workflow folder + const workflowEntries = workflowFolders + .map((folder) => { + const workflowPath = path.join(workflowsDir, folder); + const metadata = parseWorkflowMetadata(workflowPath); + if (!metadata) return null; + + return { + folder, + name: metadata.name, + description: metadata.description, + triggers: metadata.triggers, + tags: metadata.tags, + assets: metadata.assets, + }; + }) + .filter((entry) => entry !== null) + .sort((a, b) => a.name.localeCompare(b.name)); + + console.log(`Found ${workflowEntries.length} workflow(s)`); + + if (workflowEntries.length === 0) { + return ""; + } + + // Create table header + let content = + "| Name | Description | Triggers | Bundled Assets |\n| ---- | ----------- | -------- | -------------- |\n"; + + // Generate table rows for each workflow + for (const workflow of workflowEntries) { + const link = `../workflows/${workflow.folder}/README.md`; + const triggers = workflow.triggers.length > 0 ? workflow.triggers.join(", ") : "N/A"; + const assetsList = + workflow.assets.length > 0 + ? workflow.assets.map((a) => `\`${a}\``).join("
") + : "None"; + + content += `| [${workflow.name}](${link}) | ${formatTableCell( + workflow.description + )} | ${triggers} | ${assetsList} |\n`; + } + + return `${TEMPLATES.workflowsSection}\n${TEMPLATES.workflowsUsage}\n\n${content}`; +} + /** * Generate the skills section with a table of all skills */ @@ -921,6 +984,7 @@ async function main() { const promptsHeader = TEMPLATES.promptsSection.replace(/^##\s/m, "# "); const agentsHeader = TEMPLATES.agentsSection.replace(/^##\s/m, "# "); const hooksHeader = TEMPLATES.hooksSection.replace(/^##\s/m, "# "); + const workflowsHeader = TEMPLATES.workflowsSection.replace(/^##\s/m, "# "); const skillsHeader = TEMPLATES.skillsSection.replace(/^##\s/m, "# "); const pluginsHeader = TEMPLATES.pluginsSection.replace( /^##\s/m, @@ -959,6 +1023,15 @@ async function main() { registryNames ); + // Generate workflows README + const workflowsReadme = buildCategoryReadme( + generateWorkflowsSection, + WORKFLOWS_DIR, + workflowsHeader, + TEMPLATES.workflowsUsage, + registryNames + ); + // Generate skills README const skillsReadme = buildCategoryReadme( generateSkillsSection, @@ -990,6 +1063,7 @@ async function main() { writeFileIfChanged(path.join(DOCS_DIR, "README.prompts.md"), promptsReadme); writeFileIfChanged(path.join(DOCS_DIR, "README.agents.md"), agentsReadme); writeFileIfChanged(path.join(DOCS_DIR, "README.hooks.md"), hooksReadme); + writeFileIfChanged(path.join(DOCS_DIR, "README.workflows.md"), workflowsReadme); writeFileIfChanged(path.join(DOCS_DIR, "README.skills.md"), skillsReadme); writeFileIfChanged( path.join(DOCS_DIR, "README.plugins.md"), diff --git a/eng/yaml-parser.mjs b/eng/yaml-parser.mjs index 8ef9f8a7..ded43269 100644 --- a/eng/yaml-parser.mjs +++ b/eng/yaml-parser.mjs @@ -253,6 +253,67 @@ function parseHookMetadata(hookPath) { ); } +/** + * Parse workflow metadata from a workflow folder + * @param {string} workflowPath - Path to the workflow folder + * @returns {object|null} Workflow metadata or null on error + */ +function parseWorkflowMetadata(workflowPath) { + return safeFileOperation( + () => { + const readmeFile = path.join(workflowPath, "README.md"); + if (!fs.existsSync(readmeFile)) { + return null; + } + + const frontmatter = parseFrontmatter(readmeFile); + + // Validate required fields + if (!frontmatter?.name || !frontmatter?.description) { + console.warn( + `Invalid workflow at ${workflowPath}: missing name or description in frontmatter` + ); + return null; + } + + // Extract triggers from frontmatter if present + const triggers = frontmatter.triggers || []; + + // List bundled assets (all files except README.md), recursing through subdirectories + const getAllFiles = (dirPath, arrayOfFiles = []) => { + const files = fs.readdirSync(dirPath); + + files.forEach((file) => { + const filePath = path.join(dirPath, file); + if (fs.statSync(filePath).isDirectory()) { + arrayOfFiles = getAllFiles(filePath, arrayOfFiles); + } else { + const relativePath = path.relative(workflowPath, filePath); + if (relativePath !== "README.md") { + arrayOfFiles.push(relativePath.replace(/\\/g, "/")); + } + } + }); + + return arrayOfFiles; + }; + + const assets = getAllFiles(workflowPath).sort(); + + return { + name: frontmatter.name, + description: frontmatter.description, + triggers, + tags: frontmatter.tags || [], + assets, + path: workflowPath, + }; + }, + workflowPath, + null + ); +} + /** * Parse a generic YAML file (used for tools.yml and other config files) * @param {string} filePath - Path to the YAML file @@ -276,6 +337,7 @@ export { parseFrontmatter, parseSkillMetadata, parseHookMetadata, + parseWorkflowMetadata, parseYamlFile, safeFileOperation, }; diff --git a/workflows/.gitkeep b/workflows/.gitkeep new file mode 100644 index 00000000..e69de29b