feat(FN-4150): complete Step 2 — preserve plugin workflow step mode metadata

Fusion-Task-Id: FN-4150
Fusion-Task-Lineage: 153c239b-f6cd-49e4-bc13-216e0d2ca815
This commit is contained in:
Fusion
2026-05-13 20:21:02 -07:00
committed by gsxdsm
parent 26a1502f8f
commit 9992ea018f
4 changed files with 83 additions and 10 deletions

View File

@@ -128,7 +128,7 @@ describe("TaskStore Workflow Steps", () => {
expect(found).toBeUndefined();
});
it("should resolve plugin workflow steps from injected templates", async () => {
it("should resolve plugin script-mode workflow steps from injected templates", async () => {
store.setPluginWorkflowStepTemplates([
{
pluginId: "my-plugin",
@@ -136,6 +136,58 @@ describe("TaskStore Workflow Steps", () => {
id: "plugin:my-plugin:my-step",
name: "My Plugin Step",
description: "Plugin-provided step",
mode: "script",
phase: "pre-merge",
scriptName: "my-plugin:run-step",
prompt: "",
toolMode: "readonly",
defaultOn: false,
modelProvider: "anthropic",
modelId: "claude-sonnet-4-5",
category: "Plugin",
icon: "puzzle",
},
},
]);
const listed = await store.listWorkflowSteps();
const listedStep = listed.find((candidate) => candidate.id === "plugin:my-plugin:my-step");
expect(listedStep).toMatchObject({
id: "plugin:my-plugin:my-step",
templateId: "my-step",
name: "My Plugin Step",
mode: "script",
phase: "pre-merge",
scriptName: "my-plugin:run-step",
defaultOn: false,
modelProvider: "anthropic",
modelId: "claude-sonnet-4-5",
});
const step = await store.getWorkflowStep("plugin:my-plugin:my-step");
expect(step).toMatchObject({
id: "plugin:my-plugin:my-step",
templateId: "my-step",
mode: "script",
phase: "pre-merge",
scriptName: "my-plugin:run-step",
defaultOn: false,
modelProvider: "anthropic",
modelId: "claude-sonnet-4-5",
enabled: true,
});
});
it("should resolve plugin prompt-mode workflow steps from injected templates", async () => {
store.setPluginWorkflowStepTemplates([
{
pluginId: "my-plugin",
template: {
id: "plugin:my-plugin:prompt-step",
name: "My Prompt Step",
description: "Prompt plugin step",
mode: "prompt",
phase: "pre-merge",
prompt: "Run plugin checks",
toolMode: "readonly",
category: "Plugin",
@@ -144,14 +196,12 @@ describe("TaskStore Workflow Steps", () => {
},
]);
const step = await store.getWorkflowStep("plugin:my-plugin:my-step");
const step = await store.getWorkflowStep("plugin:my-plugin:prompt-step");
expect(step).toMatchObject({
id: "plugin:my-plugin:my-step",
templateId: "my-step",
name: "My Plugin Step",
id: "plugin:my-plugin:prompt-step",
templateId: "prompt-step",
mode: "prompt",
phase: "pre-merge",
enabled: true,
prompt: "Run plugin checks",
});
});

View File

@@ -1042,7 +1042,14 @@ export class PluginLoader extends EventEmitter<{
name: step.name,
description: step.description,
prompt: step.prompt ?? "",
mode: step.mode,
phase: step.phase,
scriptName: step.scriptName,
toolMode: step.toolMode,
defaultOn: step.defaultOn,
modelProvider: step.modelProvider,
modelId: step.modelId,
enabled: step.enabled,
category: "Plugin",
icon: "puzzle",
},

View File

@@ -7163,11 +7163,15 @@ ${stepsSection}`;
templateId: stepId,
name: entry.template.name,
description: entry.template.description,
mode: "prompt",
phase: "pre-merge",
prompt: entry.template.prompt,
mode: entry.template.mode ?? "prompt",
phase: entry.template.phase ?? "pre-merge",
prompt: entry.template.prompt ?? "",
scriptName: entry.template.scriptName,
toolMode: entry.template.toolMode,
enabled: entry.template.enabled ?? true,
defaultOn: entry.template.defaultOn,
modelProvider: entry.template.modelProvider,
modelId: entry.template.modelId,
createdAt: now,
updatedAt: now,
};

View File

@@ -396,8 +396,20 @@ export interface WorkflowStepTemplate {
description: string;
/** Full agent prompt template */
prompt: string;
/** Execution mode for plugin-contributed templates; defaults to prompt. */
mode?: WorkflowStepMode;
/** Task lifecycle phase for plugin-contributed templates; defaults to pre-merge. */
phase?: "pre-merge" | "post-merge";
/** Script name for script-mode plugin templates. */
scriptName?: string;
/** Tool set available when the template runs as a prompt-mode step. */
toolMode?: WorkflowStepToolMode;
/** Whether this template should be auto-selected for new tasks. */
defaultOn?: boolean;
/** AI model provider override for prompt-mode templates. */
modelProvider?: string;
/** AI model ID override for prompt-mode templates. */
modelId?: string;
/** Grouping category (e.g., "Quality", "Security") */
category: string;
/** Optional icon identifier for UI (e.g., "file-text", "shield") */