Skip to content
Open
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: 6 additions & 0 deletions .github/plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,12 @@
"ref": "v0.0.179"
}
},
{
"name": "modernization-workflow",
"source": "plugins/modernization-workflow",
"description": "Visualize and guide the GitHub Copilot Modernize CLI Assess, Plan, and Execute workflow for .NET, Java, and C++ application repositories.",
"version": "1.0.0"
},
{
"name": "modernize-dotnet",
"description": "AI-powered .NET modernization and upgrade assistant. Helps upgrade .NET Framework and .NET applications to the latest versions of .NET.",
Expand Down
1 change: 1 addition & 0 deletions docs/README.plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-plugins) for guidelines on how t
| [java-modernization-studio](../plugins/java-modernization-studio/README.md) | 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. | 1 items | app-modernization, assessment-dashboard, azure-migration, java-modernization, legacy-java, modernization-cockpit, validation-gates |
| [kotlin-mcp-development](../plugins/kotlin-mcp-development/README.md) | Complete toolkit for building Model Context Protocol (MCP) servers in Kotlin using the official io.modelcontextprotocol:kotlin-sdk library. Includes instructions for best practices, a prompt for generating servers, and an expert chat mode for guidance. | 2 items | kotlin, mcp, model-context-protocol, kotlin-multiplatform, server-development, ktor |
| [mcp-m365-copilot](../plugins/mcp-m365-copilot/README.md) | Comprehensive collection for building declarative agents with Model Context Protocol integration for Microsoft 365 Copilot | 4 items | mcp, m365-copilot, declarative-agents, api-plugins, model-context-protocol, adaptive-cards |
| [modernization-workflow](../plugins/modernization-workflow/README.md) | Visualize and guide the GitHub Copilot Modernize CLI Assess, Plan, and Execute workflow for .NET, Java, and C++ application repositories. | 1 items | app-modernization, assessment-dashboard, azure-migration, cpp-modernization, dotnet-modernization, java-modernization, modernization-canvas, modernize-cli |
| [napkin](../plugins/napkin/README.md) | Visual whiteboard collaboration for Copilot CLI. Opens an interactive whiteboard in your browser where you can draw, sketch, and add sticky notes — then share everything back with Copilot. Copilot sees your drawings and responds with analysis, suggestions, and ideas. | 1 items | whiteboard, visual, collaboration, brainstorming, non-technical, drawing, sticky-notes, accessibility, copilot-cli, ux |
| [noob-mode](../plugins/noob-mode/README.md) | Plain-English translation layer for non-technical Copilot CLI users. Translates every approval prompt, error message, and technical output into clear, jargon-free English with color-coded risk indicators. | 1 items | accessibility, plain-english, non-technical, beginner, translation, copilot-cli, ux |
| [openapi-to-application-csharp-dotnet](../plugins/openapi-to-application-csharp-dotnet/README.md) | Generate production-ready .NET applications from OpenAPI specifications. Includes ASP.NET Core project scaffolding, controller generation, entity framework integration, and C# best practices. | 2 items | openapi, code-generation, api, csharp, dotnet, aspnet |
Expand Down
21 changes: 21 additions & 0 deletions extensions/modernization-workflow/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 EMEA App GBB

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
26 changes: 26 additions & 0 deletions extensions/modernization-workflow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Modernization Workflow canvas

An interactive GitHub Copilot CLI canvas that visualizes the documented
Modernize CLI lifecycle:

1. **Assess** the application and generate evidence.
2. **Plan** a reviewable modernization strategy and task list.
3. **Execute** transformations with build, security, and result validation.

The canvas detects .NET, Java, and C++ repository signals, generates commands
from the current workflow configuration, and refreshes progress from artifacts
under `.github/modernize/`.

## Canvas ID

`modernization-workflow`

## Agent actions

- `get_workflow_state`
- `configure_workflow`
- `set_step_status`
- `refresh_from_artifacts`

Workflow state is stored in `.github/modernize/canvas-<stateId>.json` in the
application repository so progress survives canvas and CLI restarts.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions extensions/modernization-workflow/copilot-extension.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "modernization-workflow",
"version": 1
}
167 changes: 167 additions & 0 deletions extensions/modernization-workflow/extension.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { basename } from "node:path";
import { CanvasError, createCanvas, joinSession } from "@github/copilot-sdk/extension";
import { createInstanceServer, pushState } from "./lib/server.mjs";
import {
configureWorkflow,
getWorkflowState,
refreshWorkflow,
resolveWorkspace,
setStepStatus,
} from "./lib/state.mjs";

const instances = new Map();
let sessionRef;
let workingDirectory = process.cwd();

function instance(ctx) {
const value = instances.get(ctx.instanceId);
if (!value) throw new CanvasError("not_open", "Open the Modernization Workflow canvas first.");
return value;
}

async function updateInstance(rec, operation) {
const state = await operation(rec.workspace, rec.stateId);
await pushState(rec, state);
return state;
}

const session = await joinSession({
canvases: [
createCanvas({
id: "modernization-workflow",
displayName: "Modernization Workflow",
description: "Track and guide a Modernize CLI Assess, Plan, and Execute workflow from real repository artifacts.",
inputSchema: {
type: "object",
additionalProperties: false,
properties: {
workspace: { type: "string", description: "Absolute local application repository path." },
stateId: {
type: "string",
pattern: "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$",
description: "Stable workflow identifier used for persisted state.",
},
language: { type: "string", enum: ["auto", "dotnet", "java", "cpp"] },
source: { type: "string" },
goal: { type: "string" },
upgradeTarget: { type: "string" },
planName: { type: "string", pattern: "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
},
},
actions: [
{
name: "get_workflow_state",
description: "Return the current workflow configuration, generated commands, artifacts, and step progress.",
handler: async (ctx) => {
const rec = instance(ctx);
return getWorkflowState(rec.workspace, rec.stateId);
},
},
{
name: "configure_workflow",
description: "Configure the source, language, modernization goal, and plan name.",
inputSchema: {
type: "object",
additionalProperties: false,
properties: {
language: { type: "string", enum: ["auto", "dotnet", "java", "cpp"] },
source: { type: "string", minLength: 1 },
goal: { type: "string", minLength: 1 },
upgradeTarget: { type: "string" },
planName: { type: "string", pattern: "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
delegate: { type: "string", enum: ["local", "cloud"] },
},
},
handler: async (ctx) => {
const rec = instance(ctx);
return updateInstance(rec, (workspace, stateId) =>
configureWorkflow(workspace, stateId, ctx.input ?? {}));
},
},
{
name: "set_step_status",
description: "Set an Assess, Plan, or Execute step to pending, active, complete, or blocked.",
inputSchema: {
type: "object",
additionalProperties: false,
required: ["step", "status"],
properties: {
step: { type: "string", enum: ["assess", "plan", "execute"] },
status: { type: "string", enum: ["pending", "active", "complete", "blocked"] },
},
},
handler: async (ctx) => {
const rec = instance(ctx);
return updateInstance(rec, (workspace, stateId) =>
setStepStatus(workspace, stateId, ctx.input.step, ctx.input.status));
},
},
{
name: "refresh_from_artifacts",
description: "Re-scan the repository and update progress from Modernize CLI output artifacts.",
handler: async (ctx) => {
const rec = instance(ctx);
return updateInstance(rec, refreshWorkflow);
},
},
],
open: async (ctx) => {
const workspace = resolveWorkspace(ctx.input?.workspace, workingDirectory);
const stateId = ctx.input?.stateId ?? "default";
let rec = instances.get(ctx.instanceId);

if (rec && (rec.workspace !== workspace || rec.stateId !== stateId)) {
for (const client of rec.clients) client.end();
await new Promise((resolve) => rec.server.close(resolve));
instances.delete(ctx.instanceId);
rec = null;
}

if (!rec) {
rec = await createInstanceServer({
workspace,
stateId,
initialConfig: ctx.input ?? {},
onConfigure: (input) => configureWorkflow(workspace, stateId, input),
onSetStep: (step, status) => setStepStatus(workspace, stateId, step, status),
onRefresh: () => refreshWorkflow(workspace, stateId),
});
instances.set(ctx.instanceId, rec);
}

return {
title: "Modernization Workflow",
status: basename(workspace),
url: rec.url,
};
},
onClose: async (ctx) => {
const rec = instances.get(ctx.instanceId);
if (!rec) return;
instances.delete(ctx.instanceId);
for (const client of rec.clients) client.end();
await new Promise((resolve) => rec.server.close(resolve));
},
}),
],
hooks: {
onSessionStart: async (input) => {
if (input.workingDirectory) workingDirectory = input.workingDirectory;
return {
additionalContext:
"When a user is modernizing an application, use the Modernization Workflow canvas to make Assess, Plan, and Execute progress visible. Refresh it after Modernize CLI commands change repository artifacts.",
};
},
},
});

sessionRef = session;

session.on("session.idle", async () => {
for (const rec of instances.values()) {
const state = await refreshWorkflow(rec.workspace, rec.stateId);
await pushState(rec, state);
}
});

await session.log("Modernization Workflow canvas ready", { ephemeral: true });
Loading
Loading