mirror of
https://github.com/github/awesome-copilot.git
synced 2026-09-16 11:51:03 +00:00
168 lines
7.2 KiB
JavaScript
168 lines
7.2 KiB
JavaScript
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 });
|