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
6 changes: 4 additions & 2 deletions .codespellrc
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@
# Wee, Sherif - proper name (Wee, Sherif, contributor names should not be flagged as typos)

# queston - intentional misspelling example in skills/arize-dataset/SKILL.md demonstrating typo detection in field names


# extenions - intentional misspelled key name used in plugin validators to detect invalid manifests

# nin - MongoDB $nin operator in security instructions NoSQL injection detection regex

# Vertexes - FreeCAD shape sub-elements used as property of obj.Shape
Expand All @@ -60,7 +62,7 @@

# Vally/vally - Name of product

ignore-words-list = numer,wit,aks,edn,ser,ois,gir,rouge,categor,aline,ative,afterall,deques,dateA,dateB,TE,FillIn,alle,vai,LOD,InOut,pixelX,aNULL,Wee,Sherif,queston,Vertexes,nin,FO,CAF,Parth,ans,gud,Vally,vally
ignore-words-list = numer,wit,aks,edn,ser,ois,gir,rouge,categor,aline,ative,afterall,deques,dateA,dateB,TE,FillIn,alle,vai,LOD,InOut,pixelX,aNULL,Wee,Sherif,queston,extenions,Vertexes,nin,FO,CAF,Parth,ans,gud,Vally,vally

# Skip certain files and directories
# *.tm7 - MTM DataContract exports; embedded base64 icon blobs trigger false positives
Expand Down
36 changes: 18 additions & 18 deletions .github/plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"name": "accessibility-kanban",
"source": "extensions/accessibility-kanban",
"description": "Kanban board to manage accessibility issues, allow you to plan, track, and complete remediation work.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "acreadiness-cockpit",
Expand Down Expand Up @@ -85,13 +85,13 @@
"name": "apng-studio",
"source": "extensions/apng-studio",
"description": "Interactive GitHub Copilot app canvas extension for building Animated PNG (APNG) files from frames. Draw or upload frames, tune per-frame timing and compositing, preview live, send the result to your phone by QR, and export an animated .png.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "arcade-canvas",
"source": "extensions/arcade-canvas",
"description": "Play five retro Phaser mini-games in a Copilot canvas while agents work.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "arch",
Expand Down Expand Up @@ -158,7 +158,7 @@
"name": "backlog-swipe-triage",
"source": "extensions/backlog-swipe-triage",
"description": "Quickly swipe through backlog issues to triage decisions like assign, needs-info, defer, close, or ignore.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "cast-imaging",
Expand Down Expand Up @@ -195,7 +195,7 @@
"name": "chromium-control-canvas",
"source": "extensions/chromium-control-canvas",
"description": "Opens a real Chromium window you can navigate and interact with from a Copilot canvas control panel and agent actions.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "clojure-interactive-programming",
Expand Down Expand Up @@ -239,13 +239,13 @@
"name": "color-orb",
"source": "extensions/color-orb",
"description": "A visual orb that users can ask the agent to recolor while showing a live activity log in the canvas.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "connector-namespaces",
"source": "extensions/connector-namespaces",
"description": "Browse, connect, and open MCP connectors from an Azure Connector Namespace.",
"version": "1.1.0"
"version": "1.1.1"
},
{
"name": "context-engineering",
Expand Down Expand Up @@ -373,7 +373,7 @@
"name": "diagram-viewer",
"source": "extensions/diagram-viewer",
"description": "Render diagrams, click nodes to drill down, and view agent-generated explanations directly in the canvas.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "dotnet",
Expand Down Expand Up @@ -520,7 +520,7 @@
"name": "feedback-themes",
"source": "extensions/feedback-themes",
"description": "Explore grouped customer feedback signals by impact and drill into a theme to guide product next steps.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "figma",
Expand Down Expand Up @@ -593,7 +593,7 @@
"name": "gesture-review",
"source": "extensions/gesture-review",
"description": "Review pull requests with a live camera feed and approve or reject using thumbs-up/thumbs-down gestures.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "gh-skills-builder",
Expand Down Expand Up @@ -701,7 +701,7 @@
"name": "java-modernization-studio",
"source": "extensions/java-modernization-studio",
"description": "Drive the GitHub Copilot App Modernization for Java workflow from an interactive canvas: environment readiness, repo assessment, prioritized plan and progress, validation gates, and one-click predefined-task runs grounded in the repo's real artifacts.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "kotlin-mcp-development",
Expand Down Expand Up @@ -979,13 +979,13 @@
"name": "release-notes-showcase",
"source": "extensions/release-notes-showcase",
"description": "Compose and refine launch-ready release notes with contributor callouts and export-friendly output.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "repo-actions-hub",
"source": "extensions/repo-actions-hub",
"description": "Browse repository GitHub Actions workflows, inspect recent runs, and trigger manual workflow_dispatch runs from a Copilot canvas.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "roundup",
Expand Down Expand Up @@ -1027,7 +1027,7 @@
"name": "site-studio",
"source": "extensions/site-studio",
"description": "Plan, draft, and track a personal website section by section — a shared canvas where you and your agent author content, watch progress, and review every change.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "skill-image-gen",
Expand Down Expand Up @@ -1117,13 +1117,13 @@
"name": "tiny-tool-town-submitter",
"source": "extensions/tiny-tool-town-submitter",
"description": "Inspect a repository, improve Tiny Tool Town readiness, submit its listing issue, and launch remediation work.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "token-pacman",
"source": "extensions/token-pacman",
"description": "Visualizes live session AI-credit usage as a Pac-Man board with pellets, ghosts, fruit milestones, and game-over limits.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "typescript-mcp-development",
Expand Down Expand Up @@ -1310,7 +1310,7 @@
"name": "where-was-i",
"source": "extensions/where-was-i",
"description": "Reconstruct your dev context (branch, commits, uncommitted work, PR clues) and trigger a resume prompt to continue quickly.",
"version": "1.0.0"
"version": "1.0.1"
},
{
"name": "winappcli",
Expand Down Expand Up @@ -1374,7 +1374,7 @@
"name": "work-hub",
"source": "extensions/work-hub",
"description": "Generic cross-repo command center canvas for GitHub Copilot with onboarding, focus planning, repo health, work signals, and session cleanup.",
"version": "1.0.0"
"version": "1.0.1"
}
]
}
2 changes: 2 additions & 0 deletions .github/workflows/external-plugin-command-router.yml
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@ jobs:
vally_lint_status: 'infra_error',
smoke_status: 'infra_error',
version_match_status: 'infra_error',
canvas_structure_status: 'infra_error',
failure_class: 'infra',
summary: 'Quality-gate workflow failed unexpectedly. Re-run intake to retry.',
};
Expand All @@ -764,6 +765,7 @@ jobs:
vally_lint_status: 'infra_error',
smoke_status: 'infra_error',
version_match_status: 'infra_error',
canvas_structure_status: 'infra_error',
failure_class: 'infra',
summary: 'Quality-gate workflow did not return results. Re-run intake to retry.',
};
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/external-plugin-intake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ jobs:
vally_lint_status: 'infra_error',
smoke_status: 'infra_error',
version_match_status: 'infra_error',
canvas_structure_status: 'infra_error',
failure_class: 'infra',
summary: 'Quality-gate workflow failed unexpectedly. Re-run intake to retry.',
};
Expand All @@ -127,6 +128,7 @@ jobs:
vally_lint_status: 'infra_error',
smoke_status: 'infra_error',
version_match_status: 'infra_error',
canvas_structure_status: 'infra_error',
failure_class: 'infra',
summary: 'Quality-gate workflow did not return results. Re-run intake to retry.',
};
Expand Down
15 changes: 11 additions & 4 deletions .github/workflows/external-plugin-pr-quality-gates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ jobs:
failure_class: 'infra',
checked_plugins: [],
version_match_status: 'infra_error',
canvas_structure_status: 'infra_error',
summary: 'External plugin PR change detection failed unexpectedly. Re-run this workflow.',
};
} else if (shouldRun) {
Expand All @@ -167,6 +168,7 @@ jobs:
failure_class: 'infra',
checked_plugins: [],
version_match_status: 'infra_error',
canvas_structure_status: 'infra_error',
summary: 'External plugin PR quality checks failed unexpectedly. Re-run this workflow.',
};
} else if (process.env.QUALITY_RESULT_JSON) {
Expand All @@ -177,6 +179,7 @@ jobs:
failure_class: 'infra',
checked_plugins: [],
version_match_status: 'infra_error',
canvas_structure_status: 'infra_error',
summary: 'External plugin PR quality checks did not return a result payload.',
};
}
Expand Down Expand Up @@ -245,15 +248,16 @@ jobs:
const sourceUrl = String(entry?.source_tree_url || '');
const locator = String(entry?.source?.sha || entry?.source?.ref || 'repository');
const sourceCell = sourceUrl ? `[${locator}](${sourceUrl})` : locator;
return `| ${name} | ${quality.vally_lint_status || 'not_run'} | ${quality.smoke_status || 'not_run'} | ${quality.version_match_status || 'not_run'} | ${quality.overall_status || 'not_run'} | ${sourceCell} |`;
return `| ${name} | ${quality.vally_lint_status || 'not_run'} | ${quality.smoke_status || 'not_run'} | ${quality.version_match_status || 'not_run'} | ${quality.canvas_structure_status || 'not_run'} | ${quality.overall_status || 'not_run'} | ${sourceCell} |`;
})
: ['| _none_ | not_run | not_run | not_run | not_run | _n/a_ |'];
: ['| _none_ | not_run | not_run | not_run | not_run | not_run | _n/a_ |'];
const failureDetails = checkedPlugins.flatMap((entry) => {
const name = String(entry?.name || 'unknown');
const quality = entry?.quality || {};
const shouldShowVally = quality.vally_lint_status === 'fail' || quality.vally_lint_status === 'infra_error' || String(quality.vally_lint_output || '').trim().length > 0;
const shouldShowSmoke = quality.smoke_status === 'fail' || quality.smoke_status === 'infra_error' || String(quality.smoke_output || '').trim().length > 0;
const shouldShowVersionMatch = quality.version_match_status === 'fail' || quality.version_match_status === 'infra_error' || String(quality.version_match_output || '').trim().length > 0;
const shouldShowCanvasStructure = quality.canvas_structure_status === 'fail' || quality.canvas_structure_status === 'infra_error' || String(quality.canvas_structure_output || '').trim().length > 0;

const details = [];
if (shouldShowVally) {
Expand All @@ -265,6 +269,9 @@ jobs:
if (shouldShowVersionMatch) {
details.push(formatGateOutput(name, 'version match', quality.version_match_status, quality.version_match_output));
}
if (shouldShowCanvasStructure) {
details.push(formatGateOutput(name, 'canvas structure', quality.canvas_structure_status, quality.canvas_structure_output));
}
return details;
});

Expand All @@ -277,8 +284,8 @@ jobs:
'',
'### Per-plugin quality summary',
'',
'| Plugin | vally lint | install smoke test | version match | overall | source tree |',
'|---|---|---|---|---|---|',
'| Plugin | vally lint | install smoke test | version match | canvas structure | overall | source tree |',
'|---|---|---|---|---|---|---|',
...rows,
'',
...(failureDetails.length > 0
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ All agent files (`*.agent.md`) and instruction files (`*.instructions.md`) must
- Extension `plugin.json` **must** follow the convention:
- `name`, `description`, `version` are required
- `logo` **must** be exactly `"assets/preview.png"` (enforced convention)
- `extensions` **must** be exactly `"."` (per [copilot-agent-runtime#9929](https://github.com/github/copilot-agent-runtime/pull/9929))
- `extensions` **must** be exactly `"."` in source manifests (materialization rewrites this to `"extensions"` for distribution output)
- Optional: `author`, `keywords` fields
- **Must not** include `x-awesome-copilot` field (use convention-based `assets/preview.png` only)
- Each extension must have `assets/preview.png` as the primary visual asset
Expand Down
44 changes: 44 additions & 0 deletions eng/clean-materialized-plugins.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fileURLToPath } from "url";
import { ROOT_FOLDER } from "./constants.mjs";

const PLUGINS_DIR = path.join(ROOT_FOLDER, "plugins");
const EXTENSIONS_DIR = path.join(ROOT_FOLDER, "extensions");
const MATERIALIZED_SPECS = {
agents: {
path: "agents",
Expand Down Expand Up @@ -89,6 +90,30 @@ function cleanPlugin(pluginPath) {
return { removed, manifestUpdated };
}

function cleanMaterializedExtensionPlugin(extensionPath) {
const pluginJsonPath = path.join(extensionPath, ".github", "plugin", "plugin.json");
let manifestUpdated = false;
if (fs.existsSync(pluginJsonPath)) {
const plugin = JSON.parse(fs.readFileSync(pluginJsonPath, "utf8"));
if (plugin.extensions === "extensions") {
plugin.extensions = ".";
fs.writeFileSync(pluginJsonPath, JSON.stringify(plugin, null, 2) + "\n", "utf8");
manifestUpdated = true;
console.log(` Updated ${path.basename(extensionPath)}/.github/plugin/plugin.json`);
}
}

const target = path.join(extensionPath, "extensions");
if (!fs.existsSync(target) || !fs.statSync(target).isDirectory()) {
return { removed: 0, manifestUpdated };
}

const count = countFiles(target);
fs.rmSync(target, { recursive: true, force: true });
console.log(` Removed ${path.basename(extensionPath)}/extensions/ (${count} files)`);
return { removed: count, manifestUpdated };
}

function countFiles(dir) {
let count = 0;
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
Expand Down Expand Up @@ -171,6 +196,25 @@ function main() {
}
}

if (fs.existsSync(EXTENSIONS_DIR)) {
const extensionDirs = fs.readdirSync(EXTENSIONS_DIR, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.sort();

for (const dirName of extensionDirs) {
const extensionPath = path.join(EXTENSIONS_DIR, dirName);
if (!fs.existsSync(path.join(extensionPath, "extension.mjs"))) {
continue;
}
const { removed, manifestUpdated } = cleanMaterializedExtensionPlugin(extensionPath);
total += removed;
if (manifestUpdated) {
manifestsUpdated++;
}
}
}

console.log();
if (total === 0 && manifestsUpdated === 0) {
console.log("✅ No materialized files found. Plugins are already clean.");
Expand Down
Loading
Loading