mirror of
https://github.com/github/awesome-copilot.git
synced 2026-04-11 02:35:55 +00:00
Fix broken links beginners cli course sync (#1263)
* chore: publish from staged * Update instructions for converting links from original repo * Correct existing broken links * chore: retrigger ci * cleaning up marerialzed plugins * Fixing clean script to sort out plugin.json file too * Fixing readme * Fixing plugin.json drift * Fixing readme --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Aaron Powell <me@aaron-powell.com>
This commit is contained in:
1
.github/workflows/cli-for-beginners-sync.md
vendored
1
.github/workflows/cli-for-beginners-sync.md
vendored
@@ -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/`
|
||||
|
||||
@@ -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);
|
||||
@@ -18,7 +77,8 @@ function cleanPlugin(pluginPath) {
|
||||
console.log(` Removed ${path.basename(pluginPath)}/${subdir}/ (${count} files)`);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
|
||||
return { removed, manifestUpdated };
|
||||
}
|
||||
|
||||
function countFiles(dir) {
|
||||
@@ -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");
|
||||
|
||||
@@ -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.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
main();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user