@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.
- package/README.md +260 -0
- package/catalog/workflows/agile-task.json +130 -0
- package/catalog/workflows/bug-fix.json +153 -0
- package/catalog/workflows/context-optimization.json +130 -0
- package/catalog/workflows/feature-development.json +225 -0
- package/catalog/workflows/quick-task.json +115 -0
- package/catalog/workflows/test-coverage.json +153 -0
- package/catalog/workflows/ui-reconstruction.json +241 -0
- package/catalog.js +64 -0
- package/copier.js +179 -0
- package/diagram.js +146 -0
- package/dialog.js +63 -0
- package/engine.js +467 -0
- package/index.js +378 -0
- package/package.json +49 -0
- package/store.js +90 -0
- package/types.d.ts +133 -0
|
@@ -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 };
|