diff --git a/.changeset/fix-editor-plugin-skill-catalog.md b/.changeset/fix-editor-plugin-skill-catalog.md
new file mode 100644
index 0000000000..0004ffdd8d
--- /dev/null
+++ b/.changeset/fix-editor-plugin-skill-catalog.md
@@ -0,0 +1,5 @@
+---
+"@runfusion/fusion": patch
+---
+
+Show plugin-contributed skills (e.g. compound-engineering `ce-*`) in the workflow editor. The dashboard's discovered-skills catalog was built only from the disk-scanning package manager, so plugin skills — which the engine materializes for executor sessions separately — never appeared, and built-in workflow nodes that reference them (like `builtin:compound-engineering`) showed "— select skill —" / unresolved. The skills adapter now merges plugin skill contributions into the discovered list (deduped by bare name), and the editor's node summary + skill dropdown match namespaced skillNames (`compound-engineering:ce-work`) against the catalog's two-segment names (`ce-work/SKILL.md`) via a shared bare-name normalizer.
diff --git a/packages/cli/src/commands/daemon.ts b/packages/cli/src/commands/daemon.ts
index c709dfd695..a3d59f7273 100644
--- a/packages/cli/src/commands/daemon.ts
+++ b/packages/cli/src/commands/daemon.ts
@@ -685,6 +685,9 @@ export async function runDaemon(opts: DaemonOptions = {}) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dashboard's resolve() uses a looser onMissing signature than pi's DefaultPackageManager
packageManager: packageManager as any,
getSettingsPath: (rootDir: string) => getProjectSettingsPath(rootDir),
+ // Surface plugin-contributed skills (e.g. compound-engineering ce-*) in
+ // the editor catalog; the package manager only scans disk.
+ getPluginSkills: () => pluginLoader.getPluginSkills(),
})
: undefined;
diff --git a/packages/cli/src/commands/dashboard.ts b/packages/cli/src/commands/dashboard.ts
index 00d8dc4e43..d97ff077b7 100644
--- a/packages/cli/src/commands/dashboard.ts
+++ b/packages/cli/src/commands/dashboard.ts
@@ -1549,6 +1549,10 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dashboard's resolve() uses a looser onMissing signature than pi's DefaultPackageManager
packageManager: packageManager as any,
getSettingsPath: (rootDir: string) => getProjectSettingsPath(rootDir),
+ // Surface plugin-contributed skills (e.g. compound-engineering ce-*) in
+ // the discovered-skills catalog so the workflow editor can resolve and
+ // display them. Lazy thunk: plugins finish loading before discovery runs.
+ getPluginSkills: () => pluginLoader.getPluginSkills(),
})
: undefined;
diff --git a/packages/cli/src/commands/serve.ts b/packages/cli/src/commands/serve.ts
index f53430595b..ce74335300 100644
--- a/packages/cli/src/commands/serve.ts
+++ b/packages/cli/src/commands/serve.ts
@@ -794,6 +794,9 @@ export async function runServe(
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dashboard's resolve() uses a looser onMissing signature than pi's DefaultPackageManager
packageManager: packageManager as any,
getSettingsPath: (rootDir: string) => getProjectSettingsPath(rootDir),
+ // Surface plugin-contributed skills (e.g. compound-engineering ce-*) in
+ // the editor catalog; the package manager only scans disk.
+ getPluginSkills: () => pluginLoader.getPluginSkills(),
})
: undefined;
diff --git a/packages/dashboard/app/components/WorkflowNodeEditor.tsx b/packages/dashboard/app/components/WorkflowNodeEditor.tsx
index 0a9390a394..c2d49126b4 100644
--- a/packages/dashboard/app/components/WorkflowNodeEditor.tsx
+++ b/packages/dashboard/app/components/WorkflowNodeEditor.tsx
@@ -52,7 +52,7 @@ import { useAppSettings } from "../hooks/useAppSettings";
import { isMobileViewport, useViewportMode } from "../hooks/useViewportMode";
import { workflowNodeTypes, type WorkflowFlowNodeData, type WorkflowEditorNodeKind } from "./nodes/WorkflowNodeTypes";
import { WorkflowEditorCatalogContext } from "./nodes/WorkflowEditorCatalogContext";
-import type { NodeSummaryCatalogs } from "./nodes/node-summary";
+import { bareSkillName, type NodeSummaryCatalogs } from "./nodes/node-summary";
import {
irToFlow,
flowToIr,
@@ -3419,20 +3419,32 @@ function InnerEditor({
);
})()}
- {currentExecutor === "skill" && (
-
- )}
+ {currentExecutor === "skill" && (() => {
+ // The stored skillName may be namespaced (e.g.
+ // "compound-engineering:ce-work") while the