@leclabs/agent-flow-navigator-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,241 @@
1
+ {
2
+ "id": "ui-reconstruction",
3
+ "name": "UI Reconstruction",
4
+ "description": "Extract semantic IR from existing UI, rebuild from specification. Implements the 'Platonic Form' philosophy - capturing the essence (what) not the implementation (how).",
5
+ "nodes": {
6
+ "start": {
7
+ "type": "start",
8
+ "description": "Workflow entry point"
9
+ },
10
+ "ir_component_tree": {
11
+ "type": "task",
12
+ "name": "ir_component_tree",
13
+ "description": "Extract UI component hierarchy with element types, nesting, and framework imports. Output: Component tree with semantic roles.",
14
+ "agent": "Planner",
15
+ "stage": "semantic-ir-extraction"
16
+ },
17
+ "ir_feature_boundary": {
18
+ "type": "task",
19
+ "name": "ir_feature_boundary",
20
+ "description": "Determine logical groupings of components that form discrete features. Output: Feature map with parent-child relationships.",
21
+ "agent": "Planner",
22
+ "stage": "semantic-ir-extraction"
23
+ },
24
+ "ir_interactivity": {
25
+ "type": "task",
26
+ "name": "ir_interactivity",
27
+ "description": "Document user interactions: click handlers, form submissions, state mutations, and data flow. Output: Interaction inventory.",
28
+ "agent": "Planner",
29
+ "stage": "semantic-ir-extraction"
30
+ },
31
+ "ir_business_object": {
32
+ "type": "task",
33
+ "name": "ir_business_object",
34
+ "description": "Identify domain entities displayed or manipulated by the UI and their CRUD operations. Output: Business object catalog.",
35
+ "agent": "Planner",
36
+ "stage": "semantic-ir-extraction"
37
+ },
38
+ "ir_annotate": {
39
+ "type": "task",
40
+ "name": "ir_annotate",
41
+ "description": "Overlay visual markers on screenshot identifying components, features, and interactions. Output: Annotated screenshot.",
42
+ "agent": "Planner",
43
+ "stage": "semantic-ir-extraction"
44
+ },
45
+ "ir_ascii": {
46
+ "type": "task",
47
+ "name": "ir_ascii",
48
+ "description": "Create text-based diagrams showing UI state transitions and user flows. Output: ASCII state machine diagram.",
49
+ "agent": "Planner",
50
+ "stage": "semantic-ir-extraction"
51
+ },
52
+ "ir_review": {
53
+ "type": "gate",
54
+ "name": "Review",
55
+ "description": "Validate completeness and accuracy of Semantic IR against source UI. Criteria: All visible elements mapped, interactions documented.",
56
+ "agent": "Reviewer",
57
+ "stage": "semantic-ir-extraction",
58
+ "maxRetries": 3,
59
+ "config": {
60
+ "scrutinyLevel": 3
61
+ }
62
+ },
63
+ "uiRebuild_build": {
64
+ "type": "task",
65
+ "name": "uiRebuild_build",
66
+ "description": "Implement UI from Semantic IR specification, matching visual layout and behavior. Preserve semantic structure over visual mimicry.",
67
+ "agent": "Developer",
68
+ "stage": "ui-build-from-ir"
69
+ },
70
+ "uiRebuild_review": {
71
+ "type": "gate",
72
+ "name": "Review",
73
+ "description": "Compare rebuilt UI against original screenshot and Semantic IR. Verify functional equivalence and semantic fidelity.",
74
+ "agent": "Reviewer",
75
+ "stage": "ui-build-from-ir",
76
+ "maxRetries": 3,
77
+ "config": {
78
+ "scrutinyLevel": 4
79
+ }
80
+ },
81
+ "final_review": {
82
+ "type": "gate",
83
+ "name": "Review",
84
+ "description": "Verify rebuilt UI captures essential purpose (Platonic Form) not just appearance. Test: Could this IR reconstruct the UI in a different framework?",
85
+ "agent": "Reviewer",
86
+ "stage": "unbiased-review",
87
+ "maxRetries": 1,
88
+ "config": {
89
+ "scrutinyLevel": 2
90
+ }
91
+ },
92
+ "end_success": {
93
+ "type": "end",
94
+ "result": "success",
95
+ "description": "Workflow completed successfully"
96
+ },
97
+ "hitl_ir_failed": {
98
+ "type": "end",
99
+ "result": "blocked",
100
+ "escalation": "hitl",
101
+ "description": "IR extraction failed after max retries - requires human intervention"
102
+ },
103
+ "hitl_build_failed": {
104
+ "type": "end",
105
+ "result": "blocked",
106
+ "escalation": "hitl",
107
+ "description": "UI rebuild failed after max retries - requires human intervention"
108
+ },
109
+ "hitl_final_failed": {
110
+ "type": "end",
111
+ "result": "blocked",
112
+ "escalation": "hitl",
113
+ "description": "Final review failed after max retries - requires human intervention"
114
+ },
115
+ "lint_format": {
116
+ "type": "gate",
117
+ "name": "Lint & Format",
118
+ "description": "Run lint and format checks. Auto-fix issues where possible.",
119
+ "agent": "Developer",
120
+ "stage": "delivery",
121
+ "maxRetries": 3
122
+ },
123
+ "commit": {
124
+ "type": "task",
125
+ "name": "Commit Changes",
126
+ "description": "Commit all changes with a descriptive message",
127
+ "agent": "Developer",
128
+ "stage": "delivery"
129
+ }
130
+ },
131
+ "edges": [
132
+ {
133
+ "from": "start",
134
+ "to": "ir_component_tree"
135
+ },
136
+ {
137
+ "from": "ir_component_tree",
138
+ "to": "ir_feature_boundary"
139
+ },
140
+ {
141
+ "from": "ir_feature_boundary",
142
+ "to": "ir_interactivity"
143
+ },
144
+ {
145
+ "from": "ir_interactivity",
146
+ "to": "ir_business_object"
147
+ },
148
+ {
149
+ "from": "ir_business_object",
150
+ "to": "ir_annotate"
151
+ },
152
+ {
153
+ "from": "ir_annotate",
154
+ "to": "ir_ascii"
155
+ },
156
+ {
157
+ "from": "ir_ascii",
158
+ "to": "ir_review"
159
+ },
160
+ {
161
+ "from": "ir_review",
162
+ "to": "ir_component_tree",
163
+ "on": "failed",
164
+ "label": "Reviewer feedback is provided to the analyzer"
165
+ },
166
+ {
167
+ "from": "ir_review",
168
+ "to": "hitl_ir_failed",
169
+ "on": "failed",
170
+ "label": "IR extraction exhausted retries"
171
+ },
172
+ {
173
+ "from": "ir_review",
174
+ "to": "uiRebuild_build",
175
+ "on": "passed",
176
+ "label": "Semantic IR is verified"
177
+ },
178
+ {
179
+ "from": "uiRebuild_build",
180
+ "to": "uiRebuild_review"
181
+ },
182
+ {
183
+ "from": "uiRebuild_review",
184
+ "to": "uiRebuild_build",
185
+ "on": "failed",
186
+ "label": "Reviewer feedback is provided to the builder"
187
+ },
188
+ {
189
+ "from": "uiRebuild_review",
190
+ "to": "hitl_build_failed",
191
+ "on": "failed",
192
+ "label": "UI rebuild exhausted retries"
193
+ },
194
+ {
195
+ "from": "uiRebuild_review",
196
+ "to": "final_review",
197
+ "on": "passed",
198
+ "label": "Build review passed"
199
+ },
200
+ {
201
+ "from": "final_review",
202
+ "to": "uiRebuild_build",
203
+ "on": "failed",
204
+ "label": "Reviewer feedback is provided to the builder"
205
+ },
206
+ {
207
+ "from": "final_review",
208
+ "to": "hitl_final_failed",
209
+ "on": "failed",
210
+ "label": "Final review exhausted retries"
211
+ },
212
+ {
213
+ "from": "final_review",
214
+ "to": "lint_format",
215
+ "on": "passed",
216
+ "label": "Platonic form verified"
217
+ },
218
+ {
219
+ "from": "lint_format",
220
+ "to": "commit",
221
+ "on": "passed",
222
+ "label": "Lint passes, commit changes"
223
+ },
224
+ {
225
+ "from": "lint_format",
226
+ "to": "uiRebuild_build",
227
+ "on": "failed",
228
+ "label": "Fix lint/format issues"
229
+ },
230
+ {
231
+ "from": "lint_format",
232
+ "to": "hitl_final_failed",
233
+ "on": "failed",
234
+ "label": "Lint issues persist"
235
+ },
236
+ {
237
+ "from": "commit",
238
+ "to": "end_success"
239
+ }
240
+ ]
241
+ }
package/catalog.js ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * catalog.js - Pure catalog reader module
3
+ *
4
+ * Transforms workflow data into catalog listings and selection options.
5
+ * Actual file I/O handled by MCP handler.
6
+ */
7
+
8
+ /**
9
+ * Build a workflow summary from workflow content
10
+ * @param {string} fileId - File name without .json extension
11
+ * @param {Object} content - Workflow content from JSON
12
+ * @returns {Object} Workflow summary
13
+ */
14
+ export function buildWorkflowSummary(fileId, content) {
15
+ return {
16
+ id: content.id || fileId,
17
+ name: content.name || content.id || fileId,
18
+ description: content.description || "",
19
+ stepCount: Object.keys(content.nodes || {}).length,
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Build selection options for AskUserQuestion from workflow list
25
+ * @param {Array} workflows - Array of workflow summaries
26
+ * @returns {Array} Selection options with "All" first
27
+ */
28
+ export function buildCatalogSelectionOptions(workflows) {
29
+ return [
30
+ {
31
+ label: "All workflows (Recommended)",
32
+ description: `Copy all ${workflows.length} workflows to your project`,
33
+ },
34
+ ...workflows.map((wf) => ({
35
+ label: wf.name,
36
+ description: `${wf.description} (${wf.stepCount} steps)`,
37
+ })),
38
+ ];
39
+ }
40
+
41
+ /**
42
+ * Build the complete catalog response
43
+ * @param {Array} workflows - Array of workflow summaries
44
+ * @returns {Object} Catalog response object
45
+ */
46
+ export function buildCatalogResponse(workflows) {
47
+ return {
48
+ schemaVersion: 2,
49
+ workflows,
50
+ selectionOptions: buildCatalogSelectionOptions(workflows),
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Build empty catalog response
56
+ * @returns {Object} Empty catalog response
57
+ */
58
+ export function buildEmptyCatalogResponse() {
59
+ return {
60
+ schemaVersion: 2,
61
+ workflows: [],
62
+ selectionOptions: [],
63
+ };
64
+ }
package/copier.js ADDED
@@ -0,0 +1,179 @@
1
+ /**
2
+ * copier.js - Pure workflow copier module
3
+ *
4
+ * Generates README content and provides helpers for workflow copying.
5
+ * Actual file I/O handled by MCP handler.
6
+ */
7
+
8
+ /**
9
+ * Generate root README.md content for .flow/
10
+ * @returns {string} README content
11
+ */
12
+ export function generateFlowReadme() {
13
+ return `# Flow Plugin
14
+
15
+ DAG-based workflow orchestration for Claude Code.
16
+
17
+ ## Overview
18
+
19
+ Flow provides structured workflows that guide tasks through defined stages (planning → development → verification → delivery). Each step can be delegated to specialized subagents.
20
+
21
+ ## Quick Start
22
+
23
+ Workflows work immediately from the built-in catalog - no setup required:
24
+
25
+ \`\`\`bash
26
+ # Create a task with workflow tracking
27
+ /flow:task-create "Add user authentication" [workflow] feature-development
28
+
29
+ # Or use prefix shortcuts
30
+ feat: Add user authentication # → feature-development workflow
31
+ bug: Fix login error # → bug-fix workflow
32
+ task: Update config file # → quick-task workflow
33
+
34
+ # Run the task autonomously
35
+ /flow:run
36
+ \`\`\`
37
+
38
+ ## Commands
39
+
40
+ | Command | Description |
41
+ |---------|-------------|
42
+ | \`/flow:prime\` | Load Orchestrator context (invoke at session start) |
43
+ | \`/flow:task-create\` | Create a new task with workflow tracking |
44
+ | \`/flow:task-list\` | List all flow tasks with current status |
45
+ | \`/flow:task-get\` | Get detailed task info including workflow diagram |
46
+ | \`/flow:task-advance\` | Advance task: \`<taskId> <passed|failed> [summary]\` |
47
+ | \`/flow:run\` | Execute flow tasks autonomously |
48
+ | \`/flow:list\` | List available workflows |
49
+ | \`/flow:diagram\` | Generate mermaid diagram for a workflow |
50
+ | \`/flow:init\` | Copy workflows to .flow/workflows/ for customization |
51
+ | \`/flow:load\` | Reload workflows after editing .flow/workflows/ |
52
+
53
+ ## Available Workflows
54
+
55
+ - **quick-task** - Minimal: understand → execute → verify (best for simple tasks)
56
+ - **agile-task** - Simple: analyze → implement → test → review
57
+ - **feature-development** - Full lifecycle: requirements → planning → implementation → testing → PR
58
+ - **bug-fix** - Bug workflow: reproduce → investigate → fix → verify → PR
59
+ - **test-coverage** - Analyze coverage gaps and write tests
60
+ - **context-optimization** - Optimize agent context and instructions
61
+ - **ui-reconstruction** - Reconstruct UI components from screenshots or designs
62
+
63
+ ## Customization (Optional)
64
+
65
+ Flow's workflows work directly from the catalog in the flow->navigator mcp. If you want to create custom workflows you can run \`/flow:init\` to select a workflow from the catalog to customize for your project, your agents, and your tools.
66
+
67
+ \`\`\`bash
68
+ # Copy catalog workflows to .flow/workflows/ for editing
69
+ /flow:init
70
+
71
+ # Edit .flow/workflows/{workflow}/workflow.json
72
+ # Then reload
73
+ /flow:load
74
+ \`\`\`
75
+
76
+ **Customization options:**
77
+ - Modify step definitions in workflow.json
78
+ - Add custom \`instructions\` to steps for project-specific guidance
79
+ - Create new workflows by adding new directories
80
+
81
+ ## How It Works
82
+
83
+ 1. **Navigate API** - Stateless MCP server computes next step based on workflow DAG
84
+ 2. **Task Metadata** - Workflow state stored in Claude Code task metadata
85
+ 3. **Subagent Delegation** - Steps delegated to specialized agents (planner, developer, tester, reviewer)
86
+ 4. **Retry Logic** - Failed steps retry with configurable limits, escalate to HITL if exceeded
87
+ `;
88
+ }
89
+
90
+ /**
91
+ * Generate README.md content for .flow/workflows/
92
+ * @returns {string} README content
93
+ */
94
+ export function generateWorkflowsReadme() {
95
+ return `# Flow Workflows
96
+
97
+ This directory contains workflow definitions for the flow plugin.
98
+
99
+ ## Directory Structure
100
+
101
+ \`\`\`
102
+ .flow/workflows/
103
+ ├── README.md # This file
104
+ └── {workflow}/
105
+ └── workflow.json # DAG definition (steps + edges)
106
+ \`\`\`
107
+
108
+ ## How Step Instructions Work
109
+
110
+ Step instructions are generated automatically from:
111
+ 1. **workflow.json** - \`name\` and \`description\` fields for each step
112
+ 2. **Baseline patterns** - Default guidance based on step type (analyze, implement, test, etc.)
113
+
114
+ The \`Navigate\` API returns \`stepInstructions\` with all this combined.
115
+
116
+ ## Customizing Step Instructions
117
+
118
+ To add custom instructions for a step, add an \`instructions\` field to the node definition in \`workflow.json\`:
119
+
120
+ \`\`\`json
121
+ {
122
+ "nodes": {
123
+ "implement": {
124
+ "type": "task",
125
+ "name": "Implement Feature",
126
+ "description": "Build the feature according to the plan",
127
+ "instructions": "Run /my-skill before starting. Load context from .claude/context/impl-guide.md",
128
+ "agent": "Developer"
129
+ }
130
+ }
131
+ }
132
+ \`\`\`
133
+
134
+ ### Custom Instruction Ideas
135
+
136
+ - **Run a skill**: \`"Run /lint-fix after making changes"\`
137
+ - **Load context**: \`"Read .claude/context/api-patterns.md first"\`
138
+ - **Project conventions**: \`"Follow the error handling pattern in src/utils/errors.ts"\`
139
+ - **Specific tools**: \`"Use the Grep tool to find existing implementations"\`
140
+
141
+ ## Editing Workflows
142
+
143
+ ### workflow.json
144
+
145
+ Defines the workflow DAG:
146
+ - \`nodes\`: Map of node definitions (type, name, description, agent, maxRetries)
147
+ - \`edges\`: Array of transitions between nodes (from, to, on, label)
148
+
149
+ Do NOT edit edge logic unless you understand DAG flow control.
150
+
151
+ ## Adding Custom Workflows
152
+
153
+ 1. Create a new directory: \`.flow/workflows/my-workflow/\`
154
+ 2. Add \`workflow.json\` with steps and edges
155
+ 3. Run \`/flow:load\` to reload workflows
156
+ `;
157
+ }
158
+
159
+ /**
160
+ * Validate a workflow for copying
161
+ * @param {Object} content - Workflow content from JSON
162
+ * @returns {boolean} True if valid for copying
163
+ */
164
+ export function isValidWorkflowForCopy(content) {
165
+ return !!(content && content.nodes && content.edges);
166
+ }
167
+
168
+ /**
169
+ * Compute which workflow IDs to copy
170
+ * @param {string[]} requestedIds - Specifically requested workflow IDs (may be empty)
171
+ * @param {string[]} availableIds - All available workflow IDs in catalog
172
+ * @returns {string[]} IDs to copy
173
+ */
174
+ export function computeWorkflowsToCopy(requestedIds, availableIds) {
175
+ if (requestedIds && requestedIds.length > 0) {
176
+ return requestedIds;
177
+ }
178
+ return availableIds;
179
+ }
package/diagram.js ADDED
@@ -0,0 +1,146 @@
1
+ /**
2
+ * diagram.js - Pure diagram generation module
3
+ *
4
+ * Generates mermaid flowchart diagrams from workflow definitions.
5
+ * No I/O operations - file saving handled by MCP handler.
6
+ */
7
+
8
+ import { isTerminalNode, getTerminalType, toSubagentRef } from "./engine.js";
9
+
10
+ /**
11
+ * Sanitize a label for mermaid flowcharts
12
+ */
13
+ function sanitizeMermaidLabel(label, maxLen = 40) {
14
+ if (!label) return "";
15
+ let clean = label
16
+ .replace(/"/g, "'")
17
+ .replace(/[&]/g, "and")
18
+ .replace(/[<>]/g, "")
19
+ .replace(/[[\]{}()]/g, "")
20
+ .replace(/[|]/g, "-")
21
+ .replace(/\s+/g, " ")
22
+ .trim();
23
+
24
+ if (clean.length > maxLen) {
25
+ clean = clean.substring(0, maxLen - 3) + "...";
26
+ }
27
+ return clean;
28
+ }
29
+
30
+ /**
31
+ * Sanitize edge labels for mermaid
32
+ */
33
+ function sanitizeEdgeLabel(on) {
34
+ if (!on) return "";
35
+ return on.replace(/[|"]/g, "");
36
+ }
37
+
38
+ /**
39
+ * Generate a mermaid flowchart diagram from a workflow definition
40
+ *
41
+ * @param {Object} workflowDef - The workflow definition object
42
+ * @param {string} [currentStep] - Optional step ID to highlight
43
+ * @returns {string} Markdown string with mermaid diagram and step instructions table
44
+ */
45
+ export function generateDiagram(workflowDef, currentStep = null) {
46
+ const { nodes, edges } = workflowDef;
47
+
48
+ // Build mermaid diagram
49
+ const lines = ["flowchart TD"];
50
+
51
+ for (const [stepId, step] of Object.entries(nodes)) {
52
+ const label = sanitizeMermaidLabel(step.name || step.description || stepId);
53
+ const agent = step.agent ? `<br/><small>${step.agent}</small>` : "";
54
+ const termType = getTerminalType(step);
55
+ if (termType === "start") {
56
+ lines.push(` ${stepId}(("${label}"))`);
57
+ } else if (termType === "success") {
58
+ lines.push(` ${stepId}[["${label}"]]`);
59
+ } else if (termType === "hitl" || termType === "failure") {
60
+ lines.push(` ${stepId}{{"${label}"}}`);
61
+ } else if (step.type === "gate") {
62
+ lines.push(` ${stepId}{"${label}"}`);
63
+ } else {
64
+ lines.push(` ${stepId}["${label}${agent}"]`);
65
+ }
66
+ }
67
+
68
+ lines.push("");
69
+
70
+ for (const edge of edges) {
71
+ const { from, to, on } = edge;
72
+ const edgeLabel = sanitizeEdgeLabel(on);
73
+ if (edgeLabel) {
74
+ lines.push(` ${from} -->|${edgeLabel}| ${to}`);
75
+ } else {
76
+ lines.push(` ${from} --> ${to}`);
77
+ }
78
+ }
79
+
80
+ lines.push("");
81
+ lines.push(" classDef startStep fill:#90EE90,stroke:#228B22");
82
+ lines.push(" classDef successStep fill:#87CEEB,stroke:#4169E1");
83
+ lines.push(" classDef hitlStep fill:#FFB6C1,stroke:#DC143C");
84
+ lines.push(" classDef gateStep fill:#E6E6FA,stroke:#9370DB");
85
+ lines.push(" classDef currentStep fill:#FFD700,stroke:#FF8C00,stroke-width:3px");
86
+
87
+ const startSteps = Object.entries(nodes)
88
+ .filter(([, s]) => getTerminalType(s) === "start")
89
+ .map(([id]) => id);
90
+ const successSteps = Object.entries(nodes)
91
+ .filter(([, s]) => getTerminalType(s) === "success")
92
+ .map(([id]) => id);
93
+ const hitlSteps = Object.entries(nodes)
94
+ .filter(([, s]) => getTerminalType(s) === "hitl" || getTerminalType(s) === "failure")
95
+ .map(([id]) => id);
96
+ const gateSteps = Object.entries(nodes)
97
+ .filter(([, s]) => s.type === "gate")
98
+ .map(([id]) => id);
99
+
100
+ if (startSteps.length) lines.push(` class ${startSteps.join(",")} startStep`);
101
+ if (successSteps.length) lines.push(` class ${successSteps.join(",")} successStep`);
102
+ if (hitlSteps.length) lines.push(` class ${hitlSteps.join(",")} hitlStep`);
103
+ if (gateSteps.length) lines.push(` class ${gateSteps.join(",")} gateStep`);
104
+
105
+ if (currentStep && nodes[currentStep]) {
106
+ lines.push(` class ${currentStep} currentStep`);
107
+ }
108
+
109
+ // Build instructions table
110
+ const tableRows = [];
111
+ tableRows.push("| Stage | Step | Name | Agent | Instructions |");
112
+ tableRows.push("|-------|------|------|-------|--------------|");
113
+
114
+ // Group steps by stage for organized display - filter out terminal/start/end nodes
115
+ const stepEntries = Object.entries(nodes).filter(([, step]) => !isTerminalNode(step));
116
+
117
+ for (const [stepId, step] of stepEntries) {
118
+ const stage = step.stage || "-";
119
+ const name = step.name || stepId;
120
+ const agent = step.agent ? toSubagentRef(step.agent) : "-";
121
+ const instructions = step.instructions || step.description || "-";
122
+ // Escape pipes in table cells
123
+ const safeInstructions = instructions.replace(/\|/g, "\\|");
124
+ tableRows.push(`| ${stage} | ${stepId} | ${name} | ${agent} | ${safeInstructions} |`);
125
+ }
126
+
127
+ // Assemble output with markdown code block for mermaid
128
+ return [
129
+ `## Workflow: ${workflowDef.name || workflowDef.id}`,
130
+ "",
131
+ workflowDef.description || "",
132
+ "",
133
+ "### Diagram",
134
+ "",
135
+ "```mermaid",
136
+ ...lines,
137
+ "```",
138
+ "",
139
+ "### Step Instructions",
140
+ "",
141
+ ...tableRows,
142
+ ].join("\n");
143
+ }
144
+
145
+ // Export helpers for testing
146
+ export { sanitizeMermaidLabel, sanitizeEdgeLabel };
package/dialog.js ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * dialog.js - Pure dialog builder module
3
+ *
4
+ * Builds dialog structures for workflow selection.
5
+ * No I/O operations - just data transformation.
6
+ */
7
+
8
+ /**
9
+ * Build an option object from a workflow
10
+ * @param {Object} workflow - Workflow summary object
11
+ * @returns {Object} Option for dialog
12
+ */
13
+ function buildOption(workflow) {
14
+ return {
15
+ label: workflow.name,
16
+ description: `${workflow.description} (${workflow.stepCount} steps)`,
17
+ };
18
+ }
19
+
20
+ /**
21
+ * Build workflow selection dialog structure
22
+ *
23
+ * @param {Array} workflows - Array of workflow summaries from store.listWorkflows()
24
+ * @returns {Object} Dialog structure with workflows and dialog panels
25
+ */
26
+ export function buildWorkflowSelectionDialog(workflows) {
27
+ const byId = Object.fromEntries(workflows.map((w) => [w.id, w]));
28
+
29
+ const opt = (id) => (byId[id] ? buildOption(byId[id]) : null);
30
+
31
+ return {
32
+ schemaVersion: 2,
33
+ workflows,
34
+ dialog: [
35
+ {
36
+ question: "Which workflow?",
37
+ header: "Primary",
38
+ multiSelect: false,
39
+ options: [opt("feature-development"), opt("bug-fix"), opt("context-optimization"), opt("test-coverage")].filter(
40
+ Boolean
41
+ ),
42
+ },
43
+ {
44
+ question: "Or a simpler/specialized workflow?",
45
+ header: "Other",
46
+ multiSelect: false,
47
+ options: [opt("agile-task"), opt("quick-task"), opt("ui-reconstruction")].filter(Boolean),
48
+ },
49
+ {
50
+ question: "Include documentation?",
51
+ header: "Docs",
52
+ multiSelect: false,
53
+ options: [
54
+ { label: "Yes", description: "Generate documentation for the workflow" },
55
+ { label: "No", description: "Skip documentation" },
56
+ ],
57
+ },
58
+ ],
59
+ };
60
+ }
61
+
62
+ // Export helper for testing
63
+ export { buildOption };