From 91017050d4d992b025ec6deaa9ed06b2f060fc3d Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 21 Jun 2026 00:05:03 -0700 Subject: [PATCH 1/2] fix: show plugin-contributed skills in the workflow editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dashboard's discovered-skills catalog was built only from the disk-scanning package manager, so plugin-contributed skills (e.g. compound-engineering ce-*) — which the engine materializes for executor sessions separately — never appeared in the editor. Built-in workflow nodes that reference them (builtin:compound-engineering) showed "— select skill —" / unresolved. - skills-adapter: merge plugin skill contributions into the discovered list (deduped by bare name) via an optional getPluginSkills thunk; add shared bareSkillName normalizer. - wire getPluginSkills into all three server entry points: serve, daemon, and dashboard (the UI-serving command — verified via live end-to-end that omitting it left the editor catalog empty). - node-summary + WorkflowNodeEditor: resolve namespaced skillNames (compound-engineering:ce-work) against the catalog's two-segment names (ce-work/SKILL.md) so nodes display and select the right skill. Verified: dashboard + CLI typecheck, 136 dashboard tests, and a live dashboard E2E (discovered skills 0→11; Plan node resolves to "ce-plan" in both the canvas label and the inspector dropdown). Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/fix-editor-plugin-skill-catalog.md | 5 ++ packages/cli/src/commands/daemon.ts | 3 + packages/cli/src/commands/dashboard.ts | 4 + packages/cli/src/commands/serve.ts | 3 + .../app/components/WorkflowNodeEditor.tsx | 42 ++++++---- .../nodes/__tests__/node-summary.test.ts | 45 ++++++++++- .../app/components/nodes/node-summary.ts | 34 +++++++- .../src/__tests__/skills-adapter.test.ts | 78 ++++++++++++++++++- packages/dashboard/src/skills-adapter.ts | 54 +++++++++++++ 9 files changed, 249 insertions(+), 19 deletions(-) create mode 100644 .changeset/fix-editor-plugin-skill-catalog.md 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