Skip to content

Commit e853da4

Browse files
brunoborgesCopilot
andcommitted
Simplify workflows to flat .md files instead of folders
Workflows are now standalone .md files in workflows/ — no subfolders or README.md needed. Each file contains both the metadata frontmatter (name, description, triggers, tags) and the agentic workflow definition (on, permissions, safe-outputs) in a single file. Updated all build scripts, CI workflows, docs, and review checklists. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2cc3810 commit e853da4

File tree

8 files changed

+64
-112
lines changed

8 files changed

+64
-112
lines changed

.github/workflows/validate-agentic-workflows.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,9 @@ jobs:
2727
exit_code=0
2828
found=0
2929
30-
# Find all .md files in workflows/ subfolders (excluding README.md)
31-
for workflow_file in workflows/*/*.md; do
30+
# Find all .md files directly in workflows/
31+
for workflow_file in workflows/*.md; do
3232
[ -f "$workflow_file" ] || continue
33-
basename=$(basename "$workflow_file")
34-
[ "$basename" = "README.md" ] && continue
3533
3634
found=$((found + 1))
3735
echo "::group::Compiling $workflow_file"
@@ -45,7 +43,7 @@ jobs:
4543
done
4644
4745
if [ "$found" -eq 0 ]; then
48-
echo "No workflow .md files found to validate (README.md files are excluded)."
46+
echo "No workflow .md files found to validate."
4947
else
5048
echo "Validated $found workflow file(s)."
5149
fi

AGENTS.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The Awesome GitHub Copilot repository is a community-driven collection of custom
2121
├── instructions/ # Coding standards and guidelines (.instructions.md files)
2222
├── skills/ # Agent Skills folders (each with SKILL.md and optional bundled assets)
2323
├── hooks/ # Automated workflow hooks (folders with README.md + hooks.json)
24-
├── workflows/ # Agentic Workflows (folders with README.md + workflow .md files)
24+
├── workflows/ # Agentic Workflows (.md files for GitHub Actions automation)
2525
├── plugins/ # Installable plugin packages (folders with plugin.json)
2626
├── docs/ # Documentation for different resource types
2727
├── eng/ # Build and automation scripts
@@ -98,14 +98,14 @@ All agent files (`*.agent.md`), prompt files (`*.prompt.md`), and instruction fi
9898
- Follow the [GitHub Copilot hooks specification](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/use-hooks)
9999
- Optionally includes `tags` field for categorization
100100

101-
#### Workflow Folders (workflows/*/README.md)
102-
- Each workflow is a folder containing a `README.md` file with frontmatter and one or more `.md` workflow files
103-
- README.md must have `name` field (human-readable name)
104-
- README.md must have `description` field (wrapped in single quotes, not empty)
105-
- README.md should have `triggers` field (array of trigger types, e.g., `['schedule', 'issues']`)
106-
- Workflow `.md` files contain YAML frontmatter (`on`, `permissions`, `safe-outputs`) and natural language instructions
107-
- Folder names should be lower case with words separated by hyphens
108-
- Can include bundled assets (scripts, configuration files)
101+
#### Workflow Files (workflows/*.md)
102+
- Each workflow is a standalone `.md` file in the `workflows/` directory
103+
- Must have `name` field (human-readable name)
104+
- Must have `description` field (wrapped in single quotes, not empty)
105+
- Should have `triggers` field (array of trigger types, e.g., `['schedule', 'issues']`)
106+
- Contains agentic workflow frontmatter (`on`, `permissions`, `safe-outputs`) and natural language instructions
107+
- File names should be lower case with words separated by hyphens
108+
- Only `.md` files are accepted — `.yml`, `.yaml`, and `.lock.yml` files are blocked by CI
109109
- Optionally includes `tags` field for categorization
110110
- Follow the [GitHub Agentic Workflows specification](https://github.github.com/gh-aw)
111111

@@ -139,12 +139,11 @@ When adding a new agent, prompt, instruction, skill, hook, workflow, or plugin:
139139

140140

141141
**For Workflows:**
142-
1. Create a new folder in `workflows/` with a descriptive name
143-
2. Create `README.md` with proper frontmatter (name, description, triggers, tags)
144-
3. Add one or more `.md` workflow files with `on`, `permissions`, and `safe-outputs` frontmatter
145-
4. Add any bundled scripts or assets to the folder
146-
5. Update the README.md by running: `npm run build`
147-
6. Verify the workflow appears in the generated README
142+
1. Create a new `.md` file in `workflows/` with a descriptive name (e.g., `daily-issues-report.md`)
143+
2. Include frontmatter with `name`, `description`, `triggers`, plus agentic workflow fields (`on`, `permissions`, `safe-outputs`)
144+
3. Compile with `gh aw compile --validate` to verify it's valid
145+
4. Update the README.md by running: `npm run build`
146+
5. Verify the workflow appears in the generated README
148147

149148

150149
**For Skills:**
@@ -263,14 +262,15 @@ For hook folders (hooks/*/):
263262
- [ ] Follows [GitHub Copilot hooks specification](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/use-hooks)
264263
- [ ] Optionally includes `tags` array field for categorization
265264

266-
For workflow folders (workflows/*/):
267-
- [ ] Folder contains a README.md file with markdown front matter
265+
For workflow files (workflows/*.md):
266+
- [ ] File has markdown front matter
268267
- [ ] Has `name` field with human-readable name
269268
- [ ] Has non-empty `description` field wrapped in single quotes
270269
- [ ] Has `triggers` array field listing workflow trigger types
271-
- [ ] Folder name is lower case with hyphens
272-
- [ ] Contains at least one `.md` workflow file with `on` and `permissions` in frontmatter
270+
- [ ] File name is lower case with hyphens
271+
- [ ] Contains `on` and `permissions` in frontmatter
273272
- [ ] Workflow uses least-privilege permissions and safe outputs
273+
- [ ] No `.yml`, `.yaml`, or `.lock.yml` files included
274274
- [ ] Follows [GitHub Agentic Workflows specification](https://github.github.com/gh-aw)
275275
- [ ] Optionally includes `tags` array field for categorization
276276

CONTRIBUTING.md

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -165,35 +165,21 @@ plugins/my-plugin-id/
165165

166166
[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.
167167

168-
1. **Create a new workflow folder**: Add a new folder in the `workflows/` directory with a descriptive name (e.g., `daily-issues-report`)
169-
2. **Create a `README.md`**: Add a `README.md` with frontmatter containing `name`, `description`, `triggers`, and optionally `tags`
170-
3. **Add workflow files**: Include one or more `.md` workflow files with YAML frontmatter (`on`, `permissions`, `safe-outputs`) and natural language instructions
171-
4. **Add optional assets**: Include any helper scripts or configuration files referenced by the workflow
172-
5. **Update the README**: Run `npm run build` to update the generated README tables
168+
1. **Create your workflow file**: Add a new `.md` file in the `workflows/` directory (e.g., `daily-issues-report.md`)
169+
2. **Include frontmatter**: Add `name`, `description`, `triggers`, and optionally `tags` at the top, followed by agentic workflow frontmatter (`on`, `permissions`, `safe-outputs`) and natural language instructions
170+
3. **Test locally**: Compile with `gh aw compile --validate` to verify it's valid
171+
4. **Update the README**: Run `npm run build` to update the generated README tables
173172

174-
#### Workflow folder structure
173+
> **Note:** Only `.md` files are accepted — do not include compiled `.lock.yml` or `.yml` files. CI will block them.
175174
176-
```
177-
workflows/daily-issues-report/
178-
├── README.md # Workflow documentation with frontmatter
179-
└── daily-issues-report.md # Agentic workflow file
180-
```
181-
182-
#### README.md frontmatter example
175+
#### Workflow file example
183176

184177
```markdown
185178
---
186179
name: 'Daily Issues Report'
187180
description: 'Generates a daily summary of open issues and recent activity as a GitHub issue'
188181
triggers: ['schedule']
189182
tags: ['reporting', 'issues', 'automation']
190-
---
191-
```
192-
193-
#### Workflow file example
194-
195-
```markdown
196-
---
197183
on:
198184
schedule: daily on weekdays
199185
permissions:
@@ -220,9 +206,9 @@ Create a daily summary of open issues for the team.
220206

221207
- **Security first**: Use least-privilege permissions and safe outputs instead of direct write access
222208
- **Clear instructions**: Write clear natural language instructions in the workflow body
223-
- **Descriptive names**: Use lowercase folder names with hyphens
224-
- **Test locally**: Use `gh aw run` to test workflows before contributing
225-
- **Documentation**: Include a thorough README explaining what the workflow does and how to use it
209+
- **Descriptive names**: Use lowercase filenames with hyphens (e.g., `daily-issues-report.md`)
210+
- **Test locally**: Use `gh aw compile --validate` to verify your workflow compiles
211+
- **No compiled files**: Only submit the `.md` source — `.lock.yml` and `.yml` files are not accepted
226212
- Learn more at the [Agentic Workflows documentation](https://github.github.com/gh-aw)
227213

228214
## Submitting Your Contribution

docs/README.workflows.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
### How to Use Agentic Workflows
66

77
**What's Included:**
8-
- Each workflow is a folder containing a `README.md` and one or more `.md` workflow files
8+
- Each workflow is a single `.md` file with YAML frontmatter and natural language instructions
99
- Workflows are compiled to `.lock.yml` GitHub Actions files via `gh aw compile`
1010
- Workflows follow the [GitHub Agentic Workflows specification](https://github.github.com/gh-aw)
1111

eng/constants.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Hooks enable automated workflows triggered by specific events during GitHub Copi
135135
workflowsUsage: `### How to Use Agentic Workflows
136136
137137
**What's Included:**
138-
- Each workflow is a folder containing a \`README.md\` and one or more \`.md\` workflow files
138+
- Each workflow is a single \`.md\` file with YAML frontmatter and natural language instructions
139139
- Workflows are compiled to \`.lock.yml\` GitHub Actions files via \`gh aw compile\`
140140
- Workflows follow the [GitHub Agentic Workflows specification](https://github.github.com/gh-aw)
141141

eng/generate-website-data.mjs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ function generateHooksData(gitDates) {
195195
}
196196

197197
/**
198-
* Generate workflows metadata (folder-based, similar to hooks)
198+
* Generate workflows metadata (flat .md files)
199199
*/
200200
function generateWorkflowsData(gitDates) {
201201
const workflows = [];
@@ -210,37 +210,34 @@ function generateWorkflowsData(gitDates) {
210210
};
211211
}
212212

213-
const workflowFolders = fs.readdirSync(WORKFLOWS_DIR).filter((file) => {
214-
const filePath = path.join(WORKFLOWS_DIR, file);
215-
return fs.statSync(filePath).isDirectory();
213+
const workflowFiles = fs.readdirSync(WORKFLOWS_DIR).filter((file) => {
214+
return file.endsWith(".md") && file !== ".gitkeep";
216215
});
217216

218217
const allTriggers = new Set();
219218
const allTags = new Set();
220219

221-
for (const folder of workflowFolders) {
222-
const workflowPath = path.join(WORKFLOWS_DIR, folder);
223-
const metadata = parseWorkflowMetadata(workflowPath);
220+
for (const file of workflowFiles) {
221+
const filePath = path.join(WORKFLOWS_DIR, file);
222+
const metadata = parseWorkflowMetadata(filePath);
224223
if (!metadata) continue;
225224

226225
const relativePath = path
227-
.relative(ROOT_FOLDER, workflowPath)
226+
.relative(ROOT_FOLDER, filePath)
228227
.replace(/\\/g, "/");
229-
const readmeRelativePath = `${relativePath}/README.md`;
230228

231229
(metadata.triggers || []).forEach((t) => allTriggers.add(t));
232230
(metadata.tags || []).forEach((t) => allTags.add(t));
233231

232+
const id = path.basename(file, ".md");
234233
workflows.push({
235-
id: folder,
234+
id,
236235
title: metadata.name,
237236
description: metadata.description,
238237
triggers: metadata.triggers || [],
239238
tags: metadata.tags || [],
240-
assets: metadata.assets || [],
241239
path: relativePath,
242-
readmeFile: readmeRelativePath,
243-
lastUpdated: gitDates.get(readmeRelativePath) || null,
240+
lastUpdated: gitDates.get(relativePath) || null,
244241
});
245242
}
246243

@@ -735,7 +732,7 @@ function generateSearchIndex(
735732
id: workflow.id,
736733
title: workflow.title,
737734
description: workflow.description,
738-
path: workflow.readmeFile,
735+
path: workflow.path,
739736
lastUpdated: workflow.lastUpdated,
740737
searchText: `${workflow.title} ${workflow.description} ${workflow.triggers.join(
741738
" "

eng/update-readme.mjs

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -588,26 +588,24 @@ function generateWorkflowsSection(workflowsDir) {
588588
return "";
589589
}
590590

591-
// Get all workflow folders (directories)
592-
const workflowFolders = fs.readdirSync(workflowsDir).filter((file) => {
593-
const filePath = path.join(workflowsDir, file);
594-
return fs.statSync(filePath).isDirectory();
591+
// Get all .md workflow files (flat, no subfolders)
592+
const workflowFiles = fs.readdirSync(workflowsDir).filter((file) => {
593+
return file.endsWith(".md") && file !== ".gitkeep";
595594
});
596595

597-
// Parse each workflow folder
598-
const workflowEntries = workflowFolders
599-
.map((folder) => {
600-
const workflowPath = path.join(workflowsDir, folder);
601-
const metadata = parseWorkflowMetadata(workflowPath);
596+
// Parse each workflow file
597+
const workflowEntries = workflowFiles
598+
.map((file) => {
599+
const filePath = path.join(workflowsDir, file);
600+
const metadata = parseWorkflowMetadata(filePath);
602601
if (!metadata) return null;
603602

604603
return {
605-
folder,
604+
file,
606605
name: metadata.name,
607606
description: metadata.description,
608607
triggers: metadata.triggers,
609608
tags: metadata.tags,
610-
assets: metadata.assets,
611609
};
612610
})
613611
.filter((entry) => entry !== null)
@@ -621,20 +619,16 @@ function generateWorkflowsSection(workflowsDir) {
621619

622620
// Create table header
623621
let content =
624-
"| Name | Description | Triggers | Bundled Assets |\n| ---- | ----------- | -------- | -------------- |\n";
622+
"| Name | Description | Triggers |\n| ---- | ----------- | -------- |\n";
625623

626624
// Generate table rows for each workflow
627625
for (const workflow of workflowEntries) {
628-
const link = `../workflows/${workflow.folder}/README.md`;
626+
const link = `../workflows/${workflow.file}`;
629627
const triggers = workflow.triggers.length > 0 ? workflow.triggers.join(", ") : "N/A";
630-
const assetsList =
631-
workflow.assets.length > 0
632-
? workflow.assets.map((a) => `\`${a}\``).join("<br />")
633-
: "None";
634628

635629
content += `| [${workflow.name}](${link}) | ${formatTableCell(
636630
workflow.description
637-
)} | ${triggers} | ${assetsList} |\n`;
631+
)} | ${triggers} |\n`;
638632
}
639633

640634
return `${TEMPLATES.workflowsSection}\n${TEMPLATES.workflowsUsage}\n\n${content}`;

eng/yaml-parser.mjs

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -254,62 +254,39 @@ function parseHookMetadata(hookPath) {
254254
}
255255

256256
/**
257-
* Parse workflow metadata from a workflow folder
258-
* @param {string} workflowPath - Path to the workflow folder
257+
* Parse workflow metadata from a standalone .md workflow file
258+
* @param {string} filePath - Path to the workflow .md file
259259
* @returns {object|null} Workflow metadata or null on error
260260
*/
261-
function parseWorkflowMetadata(workflowPath) {
261+
function parseWorkflowMetadata(filePath) {
262262
return safeFileOperation(
263263
() => {
264-
const readmeFile = path.join(workflowPath, "README.md");
265-
if (!fs.existsSync(readmeFile)) {
264+
if (!fs.existsSync(filePath)) {
266265
return null;
267266
}
268267

269-
const frontmatter = parseFrontmatter(readmeFile);
268+
const frontmatter = parseFrontmatter(filePath);
270269

271270
// Validate required fields
272271
if (!frontmatter?.name || !frontmatter?.description) {
273272
console.warn(
274-
`Invalid workflow at ${workflowPath}: missing name or description in frontmatter`
273+
`Invalid workflow at ${filePath}: missing name or description in frontmatter`
275274
);
276275
return null;
277276
}
278277

279278
// Extract triggers from frontmatter if present
280279
const triggers = frontmatter.triggers || [];
281280

282-
// List bundled assets (all files except README.md), recursing through subdirectories
283-
const getAllFiles = (dirPath, arrayOfFiles = []) => {
284-
const files = fs.readdirSync(dirPath);
285-
286-
files.forEach((file) => {
287-
const filePath = path.join(dirPath, file);
288-
if (fs.statSync(filePath).isDirectory()) {
289-
arrayOfFiles = getAllFiles(filePath, arrayOfFiles);
290-
} else {
291-
const relativePath = path.relative(workflowPath, filePath);
292-
if (relativePath !== "README.md") {
293-
arrayOfFiles.push(relativePath.replace(/\\/g, "/"));
294-
}
295-
}
296-
});
297-
298-
return arrayOfFiles;
299-
};
300-
301-
const assets = getAllFiles(workflowPath).sort();
302-
303281
return {
304282
name: frontmatter.name,
305283
description: frontmatter.description,
306284
triggers,
307285
tags: frontmatter.tags || [],
308-
assets,
309-
path: workflowPath,
286+
path: filePath,
310287
};
311288
},
312-
workflowPath,
289+
filePath,
313290
null
314291
);
315292
}

0 commit comments

Comments
 (0)