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:
Fusion
2026-05-07 23:17:56 -07:00
committed by gsxdsm
parent a6d019d70a
commit ae2baa09ee
18 changed files with 1936 additions and 35 deletions

View File

@@ -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) => {

View File

@@ -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",