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