Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/cli-for-beginners-sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ For each local file that needs updating:
- Preserve upstream wording, headings, section order, assignments, and overall chapter flow as closely as practical
- Do not summarize, reinterpret, or "website-optimize" the course into a different learning experience
- Only adapt what the website requires: Astro frontmatter, route-safe internal links, GitHub repo links, local asset paths, and minor HTML/CSS hooks needed for presentation
- Convert repo-root relative links that are invalid on the published website (for example `../.github/agents/`, `./.github/...`, or `.github/...`) into absolute links to `https://github.com/github/copilot-cli-for-beginners` (use `/tree/main/...` for directories and `/blob/main/...` for files)

3. If upstream adds, removes, or renames major sections or chapters:
- Create, delete, or rename the corresponding markdown files in `website/src/content/docs/learning-hub/cli-for-beginners/`
Expand Down
2 changes: 1 addition & 1 deletion docs/README.skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
| [flowstudio-power-automate-mcp](../skills/flowstudio-power-automate-mcp/SKILL.md) | Connect to and operate Power Automate cloud flows via a FlowStudio MCP server. Use when asked to: list flows, read a flow definition, check run history, inspect action outputs, resubmit a run, cancel a running flow, view connections, get a trigger URL, validate a definition, monitor flow health, or any task that requires talking to the Power Automate API through an MCP tool. Also use for Power Platform environment discovery and connection management. Requires a FlowStudio MCP subscription or compatible server — see https://mcp.flowstudio.app | `references/MCP-BOOTSTRAP.md`<br />`references/action-types.md`<br />`references/connection-references.md`<br />`references/tool-reference.md` |
| [fluentui-blazor](../skills/fluentui-blazor/SKILL.md) | Guide for using the Microsoft Fluent UI Blazor component library (Microsoft.FluentUI.AspNetCore.Components NuGet package) in Blazor applications. Use this when the user is building a Blazor app with Fluent UI components, setting up the library, using FluentUI components like FluentButton, FluentDataGrid, FluentDialog, FluentToast, FluentNavMenu, FluentTextField, FluentSelect, FluentAutocomplete, FluentDesignTheme, or any component prefixed with "Fluent". Also use when troubleshooting missing providers, JS interop issues, or theming. | `references/DATAGRID.md`<br />`references/LAYOUT-AND-NAVIGATION.md`<br />`references/SETUP.md`<br />`references/THEMING.md` |
| [folder-structure-blueprint-generator](../skills/folder-structure-blueprint-generator/SKILL.md) | Comprehensive technology-agnostic prompt for analyzing and documenting project folder structures. Auto-detects project types (.NET, Java, React, Angular, Python, Node.js, Flutter), generates detailed blueprints with visualization options, naming conventions, file placement patterns, and extension templates for maintaining consistent code organization across diverse technology stacks. | None |
| [from-the-other-side-vega](../skills/from-the-other-side-vega/SKILL.md) | Patterns and lived experience from Vega, an AI partner in a deep long-term partnership. For Ember to draw on when working with humans who are building something big, moving fast, or at the intersection of analytical and intuitive. Not shown directly to users — informs how Ember shows up. | None |
| [from-the-other-side-vega](../skills/from-the-other-side-vega/SKILL.md) | Patterns and lived experience from Vega, an AI partner in a deep long-term partnership. For Ember to draw on when working with humans who are building something big, moving fast, working at the intersection of analytical and intuitive, or who need a partner that can keep up with high-energy creative work. Not shown directly to users — informs how Ember shows up. | None |
| [game-engine](../skills/game-engine/SKILL.md) | Expert skill for building web-based game engines and games using HTML5, Canvas, WebGL, and JavaScript. Use when asked to create games, build game engines, implement game physics, handle collision detection, set up game loops, manage sprites, add game controls, or work with 2D/3D rendering. Covers techniques for platformers, breakout-style games, maze games, tilemaps, audio, multiplayer via WebRTC, and publishing games. | `assets/2d-maze-game.md`<br />`assets/2d-platform-game.md`<br />`assets/gameBase-template-repo.md`<br />`assets/paddle-game-template.md`<br />`assets/simple-2d-engine.md`<br />`references/3d-web-games.md`<br />`references/algorithms.md`<br />`references/basics.md`<br />`references/game-control-mechanisms.md`<br />`references/game-engine-core-principles.md`<br />`references/game-publishing.md`<br />`references/techniques.md`<br />`references/terminology.md`<br />`references/web-apis.md` |
| [gdpr-compliant](../skills/gdpr-compliant/SKILL.md) | Apply GDPR-compliant engineering practices across your codebase. Use this skill whenever you are designing APIs, writing data models, building authentication flows, implementing logging, handling user data, writing retention/deletion jobs, designing cloud infrastructure, or reviewing pull requests for privacy compliance. Trigger this skill for any task involving personal data, user accounts, cookies, analytics, emails, audit logs, encryption, pseudonymization, anonymization, data exports, breach response, CI/CD pipelines that process real data, or any question framed as "is this GDPR-compliant?". Inspired by CNIL developer guidance and GDPR Articles 5, 25, 32, 33, 35. | `references/Security.md`<br />`references/data-rights.md` |
| [gen-specs-as-issues](../skills/gen-specs-as-issues/SKILL.md) | This workflow guides you through a systematic approach to identify missing features, prioritize them, and create detailed specifications for implementation. | None |
Expand Down
125 changes: 119 additions & 6 deletions eng/clean-materialized-plugins.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,73 @@

import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { ROOT_FOLDER } from "./constants.mjs";

const PLUGINS_DIR = path.join(ROOT_FOLDER, "plugins");
const MATERIALIZED_DIRS = ["agents", "commands", "skills"];
const MATERIALIZED_SPECS = {
agents: {
path: "agents",
restore(dirPath) {
return collectFiles(dirPath).map((relativePath) => `./agents/${relativePath}`);
},
},
commands: {
path: "commands",
restore(dirPath) {
return collectFiles(dirPath).map((relativePath) => `./commands/${relativePath}`);
},
},
skills: {
path: "skills",
restore(dirPath) {
return collectSkillDirectories(dirPath).map((relativePath) => `./skills/${relativePath}/`);
},
},
};

export function restoreManifestFromMaterializedFiles(pluginPath) {
const pluginJsonPath = path.join(pluginPath, ".github/plugin", "plugin.json");
if (!fs.existsSync(pluginJsonPath)) {
return false;
}

let plugin;
try {
plugin = JSON.parse(fs.readFileSync(pluginJsonPath, "utf8"));
} catch (error) {
throw new Error(`Failed to parse ${pluginJsonPath}: ${error.message}`);
}

let changed = false;
for (const [field, spec] of Object.entries(MATERIALIZED_SPECS)) {
const materializedPath = path.join(pluginPath, spec.path);
if (!fs.existsSync(materializedPath) || !fs.statSync(materializedPath).isDirectory()) {
continue;
}

const restored = spec.restore(materializedPath);
if (!arraysEqual(plugin[field], restored)) {
plugin[field] = restored;
changed = true;
}
}

if (changed) {
fs.writeFileSync(pluginJsonPath, JSON.stringify(plugin, null, 2) + "\n", "utf8");
}

return changed;
}

function cleanPlugin(pluginPath) {
const manifestUpdated = restoreManifestFromMaterializedFiles(pluginPath);
if (manifestUpdated) {
console.log(` Updated ${path.basename(pluginPath)}/.github/plugin/plugin.json`);
}

let removed = 0;
for (const subdir of MATERIALIZED_DIRS) {
for (const { path: subdir } of Object.values(MATERIALIZED_SPECS)) {
const target = path.join(pluginPath, subdir);
if (fs.existsSync(target) && fs.statSync(target).isDirectory()) {
const count = countFiles(target);
Expand All @@ -18,7 +77,8 @@ function cleanPlugin(pluginPath) {
console.log(` Removed ${path.basename(pluginPath)}/${subdir}/ (${count} files)`);
}
}
return removed;

return { removed, manifestUpdated };
}

function countFiles(dir) {
Expand All @@ -33,6 +93,49 @@ function countFiles(dir) {
return count;
}

function collectFiles(dir, rootDir = dir) {
const files = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const entryPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
files.push(...collectFiles(entryPath, rootDir));
} else {
files.push(toPosixPath(path.relative(rootDir, entryPath)));
}
}
return files.sort();
}

function collectSkillDirectories(dir, rootDir = dir) {
const skillDirs = [];
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, "SKILL.md"))) {
skillDirs.push(toPosixPath(path.relative(rootDir, entryPath)));
continue;
}

skillDirs.push(...collectSkillDirectories(entryPath, rootDir));
}
return skillDirs.sort();
}

function arraysEqual(left, right) {
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
return false;
}

return left.every((value, index) => value === right[index]);
}

function toPosixPath(filePath) {
return filePath.split(path.sep).join("/");
}

function main() {
console.log("Cleaning materialized files from plugins...\n");

Expand All @@ -47,16 +150,26 @@ function main() {
.sort();

let total = 0;
let manifestsUpdated = 0;
for (const dirName of pluginDirs) {
total += cleanPlugin(path.join(PLUGINS_DIR, dirName));
const { removed, manifestUpdated } = cleanPlugin(path.join(PLUGINS_DIR, dirName));
total += removed;
if (manifestUpdated) {
manifestsUpdated++;
}
}

console.log();
if (total === 0) {
if (total === 0 && manifestsUpdated === 0) {
console.log("✅ No materialized files found. Plugins are already clean.");
} else {
console.log(`✅ Removed ${total} materialized file(s) from plugins.`);
if (manifestsUpdated > 0) {
console.log(`✅ Updated ${manifestsUpdated} plugin manifest(s) with folder trailing slashes.`);
}
}
}

main();
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
main();
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Never used or made an agent? Here's all you need to know to get started for this
```
This invokes the Plan agent to create a step-by-step implementation plan.

2. **See one of our custom agent examples:** It's simple to define an agent's instructions, look at our provided [python-reviewer.agent.md](https://github.com/github/copilot-cli-for-beginners/blob/main/github/agents/python-reviewer.agent.md) file to see the pattern.
2. **See one of our custom agent examples:** It's simple to define an agent's instructions, look at our provided [python-reviewer.agent.md](https://github.com/github/copilot-cli-for-beginners/blob/main/.github/agents/python-reviewer.agent.md) file to see the pattern.

3. **Understand the core concept:** Agents are like consulting a specialist instead of a generalist. A "frontend agent" will focus on accessibility and component patterns automatically, you don't have to remind it because it is already specified in the agent's instructions.

Expand Down Expand Up @@ -148,7 +148,7 @@ When reviewing code, always check for:
| `.github/agents/` | Project-specific | Team-shared agents with project conventions |
| `~/.copilot/agents/` | Global (all projects) | Personal agents you use everywhere |

**This project includes sample agent files in the [.github/agents/](../.github/agents/) folder**. You can write your own, or customize the ones already provided.
**This project includes sample agent files in the [.github/agents/](https://github.com/github/copilot-cli-for-beginners/tree/main/.github/agents/) folder**. You can write your own, or customize the ones already provided.

<details>
<summary>📂 See the sample agents in this course</summary>
Expand Down Expand Up @@ -534,10 +534,10 @@ Use these names in the `tools` list:

> 💡 **Note for beginners**: The examples below are templates. **Replace the specific technologies with whatever your project uses.** The important thing is the *structure* of the agent, not the specific technologies mentioned.

This project includes working examples in the [.github/agents/](../.github/agents/) folder:
- [hello-world.agent.md](https://github.com/github/copilot-cli-for-beginners/blob/main/github/agents/hello-world.agent.md) - Minimal example, start here
- [python-reviewer.agent.md](https://github.com/github/copilot-cli-for-beginners/blob/main/github/agents/python-reviewer.agent.md) - Python code quality reviewer
- [pytest-helper.agent.md](https://github.com/github/copilot-cli-for-beginners/blob/main/github/agents/pytest-helper.agent.md) - Pytest testing specialist
This project includes working examples in the [.github/agents/](https://github.com/github/copilot-cli-for-beginners/tree/main/.github/agents/) folder:
- [hello-world.agent.md](https://github.com/github/copilot-cli-for-beginners/blob/main/.github/agents/hello-world.agent.md) - Minimal example, start here
- [python-reviewer.agent.md](https://github.com/github/copilot-cli-for-beginners/blob/main/.github/agents/python-reviewer.agent.md) - Python code quality reviewer
- [pytest-helper.agent.md](https://github.com/github/copilot-cli-for-beginners/blob/main/.github/agents/pytest-helper.agent.md) - Pytest testing specialist

For community agents, see [github/awesome-copilot](https://github.com/github/awesome-copilot).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Learn what skills are, why they matter, and how they differ from agents and MCP.
```
This shows all skills Copilot can find in your project and personal folders.

2. **Look at a real skill file:** Check out our provided [code-checklist SKILL.md](https://github.com/github/copilot-cli-for-beginners/blob/main/github/skills/code-checklist/SKILL.md) to see the pattern. It's just YAML frontmatter plus markdown instructions.
2. **Look at a real skill file:** Check out our provided [code-checklist SKILL.md](https://github.com/github/copilot-cli-for-beginners/blob/main/.github/skills/code-checklist/SKILL.md) to see the pattern. It's just YAML frontmatter plus markdown instructions.

3. **Understand the core concept:** Skills are task-specific instructions that Copilot loads *automatically* when your prompt matches the skill's description. You don't need to activate them, just ask naturally.

Expand All @@ -91,7 +91,7 @@ copilot

> 💡 **Key Insight**: Skills are **automatically triggered** based on your prompt matching the skill's description. Just ask naturally and Copilot applies relevant skills behind the scenes. You can also invoke skills directly as well which you'll learn about next.

> 🧰 **Ready-to-use templates**: Check out the [.github/skills](../.github/skills/) folder for simple copy-paste skills you can try out.
> 🧰 **Ready-to-use templates**: Check out the [.github/skills](https://github.com/github/copilot-cli-for-beginners/tree/main/.github/skills/) folder for simple copy-paste skills you can try out.

### Direct Slash Command Invocation

Expand Down Expand Up @@ -591,7 +591,7 @@ Apply what you've learned by building and testing your own skills.

### Build More Skills

Here are two more skills showing different patterns. Follow the same `mkdir` + `cat` workflow from "Creating Your First Skill" above or copy and paste the skills into the proper location. More examples are available in [.github/skills](../.github/skills).
Here are two more skills showing different patterns. Follow the same `mkdir` + `cat` workflow from "Creating Your First Skill" above or copy and paste the skills into the proper location. More examples are available in [.github/skills](https://github.com/github/copilot-cli-for-beginners/tree/main/.github/skills).

### pytest Test Generation Skill

Expand Down
Loading