feat(FN-3158): migrate roadmap domain modules to fusion-plugin-roadmap pack
The merge extracts the roadmap domain into a standalone plugin package (`plugins/fusion-plugin-roadmap`), wiring it into the CLI bundle and dashboard scaffold, with new modules for roadmap types, store, ordering, and handoff logic. A secondary change normalizes shell host bootstrap, introducing `She Fusion-Task-Id: FN-3158
This commit is contained in:
@@ -23,6 +23,7 @@ vi.mock("@fusion/core", () => ({
|
||||
|
||||
// Import SUT after mocks are in place
|
||||
import {
|
||||
BUNDLED_PLUGIN_IDS,
|
||||
ensureBundledDependencyGraphPluginInstalled,
|
||||
ensureBundledCursorRuntimePluginInstalled,
|
||||
ensureBundledPluginInstalled,
|
||||
@@ -34,6 +35,7 @@ import {
|
||||
const BUNDLED_PLUGIN_ID = "fusion-plugin-dependency-graph";
|
||||
const HERMES_PLUGIN_ID = "fusion-plugin-hermes-runtime";
|
||||
const CURSOR_PLUGIN_ID = "fusion-plugin-cursor-runtime";
|
||||
const ROADMAP_PLUGIN_ID = "fusion-plugin-roadmap";
|
||||
|
||||
function makeManifest(overrides?: Partial<{ id: string; version: string; name: string }>) {
|
||||
return {
|
||||
@@ -215,6 +217,9 @@ describe("resolvePluginEntryPath", () => {
|
||||
});
|
||||
|
||||
describe("ensureBundledDependencyGraphPluginInstalled", () => {
|
||||
it("includes roadmap plugin in bundled plugin ids", () => {
|
||||
expect(BUNDLED_PLUGIN_IDS).toContain(ROADMAP_PLUGIN_ID);
|
||||
});
|
||||
it("fresh install: registers and loads the plugin when not in DB", async () => {
|
||||
setupBundleExists();
|
||||
const store = makePluginStore();
|
||||
@@ -382,6 +387,31 @@ describe("ensureBundledDependencyGraphPluginInstalled", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("registers roadmap plugin via generic bundled installer", async () => {
|
||||
const manifest = makeManifest({ id: ROADMAP_PLUGIN_ID, name: "Roadmaps" });
|
||||
mockExistsSync.mockImplementation((p: string) => {
|
||||
if (p.endsWith("manifest.json") && p.includes(ROADMAP_PLUGIN_ID)) return true;
|
||||
if (p.endsWith("/src/index.ts") && p.includes(ROADMAP_PLUGIN_ID)) return true;
|
||||
return false;
|
||||
});
|
||||
mockReadFile.mockResolvedValue(JSON.stringify(manifest));
|
||||
mockValidatePluginManifest.mockReturnValue({ valid: true, errors: [] });
|
||||
|
||||
const store = makePluginStore();
|
||||
const loader = makePluginLoader();
|
||||
|
||||
const result = await ensureBundledPluginInstalled(
|
||||
store as unknown as import("@fusion/core").PluginStore,
|
||||
loader as unknown as import("@fusion/core").PluginLoader,
|
||||
ROADMAP_PLUGIN_ID,
|
||||
);
|
||||
|
||||
expect(result).toBe("installed");
|
||||
expect(store.registerPlugin).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ manifest: expect.objectContaining({ id: ROADMAP_PLUGIN_ID }) }),
|
||||
);
|
||||
});
|
||||
|
||||
it("registers Hermes from source entry when both src and dist entries exist", async () => {
|
||||
const manifest = makeManifest({ id: HERMES_PLUGIN_ID, name: "Hermes Runtime" });
|
||||
mockExistsSync.mockImplementation((p: string) => {
|
||||
|
||||
@@ -10,6 +10,7 @@ const CURSOR_RUNTIME_PLUGIN_ID = "fusion-plugin-cursor-runtime";
|
||||
export const BUNDLED_PLUGIN_IDS = [
|
||||
"fusion-plugin-dependency-graph",
|
||||
"fusion-plugin-whatsapp-chat",
|
||||
"fusion-plugin-roadmap",
|
||||
"fusion-plugin-hermes-runtime",
|
||||
"fusion-plugin-openclaw-runtime",
|
||||
"fusion-plugin-paperclip-runtime",
|
||||
|
||||
@@ -34,6 +34,8 @@ const dependencyGraphPluginSrc = join(__dirname, "..", "..", "plugins", "fusion-
|
||||
const dependencyGraphPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-dependency-graph");
|
||||
const whatsappChatPluginSrc = join(__dirname, "..", "..", "plugins", "fusion-plugin-whatsapp-chat");
|
||||
const whatsappChatPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-whatsapp-chat");
|
||||
const roadmapPluginSrc = join(__dirname, "..", "..", "plugins", "fusion-plugin-roadmap");
|
||||
const roadmapPluginDest = join(__dirname, "dist", "plugins", "fusion-plugin-roadmap");
|
||||
const dashboardClientStub = `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -163,6 +165,21 @@ export default defineConfig({
|
||||
);
|
||||
}
|
||||
|
||||
if (existsSync(roadmapPluginDest)) {
|
||||
rmSync(roadmapPluginDest, { recursive: true, force: true });
|
||||
}
|
||||
if (existsSync(roadmapPluginSrc)) {
|
||||
mkdirSync(roadmapPluginDest, { recursive: true });
|
||||
cpSync(join(roadmapPluginSrc, "manifest.json"), join(roadmapPluginDest, "manifest.json"));
|
||||
cpSync(join(roadmapPluginSrc, "package.json"), join(roadmapPluginDest, "package.json"));
|
||||
cpSync(join(roadmapPluginSrc, "src"), join(roadmapPluginDest, "src"), { recursive: true });
|
||||
console.log("Copied roadmap plugin to dist/plugins/fusion-plugin-roadmap/");
|
||||
} else {
|
||||
console.warn(
|
||||
`WARNING: Roadmap plugin source not found at ${roadmapPluginSrc}; bundled auto-install will be unavailable.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Bundle each runtime plugin into a self-contained ESM file so npm/npx
|
||||
// installs can load them without the workspace `@fusion/plugin-sdk`.
|
||||
for (const pluginId of RUNTIME_PLUGIN_IDS) {
|
||||
|
||||
Reference in New Issue
Block a user