@leclabs/agent-flow-navigator-mcp 1.1.0 → 1.3.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 +82 -133
- package/catalog/workflows/agile-task.json +6 -0
- package/catalog/workflows/bug-fix.json +12 -0
- package/catalog/workflows/build-review-murder-board.json +116 -0
- package/catalog/workflows/build-review-quick.json +114 -0
- package/catalog/workflows/context-optimization.json +24 -5
- package/catalog/workflows/feature-development.json +12 -0
- package/catalog/workflows/hitl-test.json +46 -0
- package/catalog/workflows/quick-task.json +6 -0
- package/catalog/workflows/refactor.json +248 -0
- package/catalog/workflows/test-coverage.json +6 -0
- package/catalog/workflows/ui-reconstruction.json +18 -0
- package/copier.js +21 -58
- package/engine.js +132 -23
- package/index.js +10 -4
- package/package.json +2 -2
- package/types.d.ts +2 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "refactor",
|
|
3
|
+
"name": "Refactor",
|
|
4
|
+
"description": "Transform outdated codebases into modern equivalents using Functional Core / Imperative Shell architecture. Separates pure business logic from side effects.",
|
|
5
|
+
"nodes": {
|
|
6
|
+
"start": {
|
|
7
|
+
"type": "start",
|
|
8
|
+
"name": "Start",
|
|
9
|
+
"description": "Refactoring workflow begins"
|
|
10
|
+
},
|
|
11
|
+
"analyze_structure": {
|
|
12
|
+
"type": "task",
|
|
13
|
+
"name": "Analyze Structure",
|
|
14
|
+
"description": "Map current architecture: modules, dependencies, entry points. Identify coupling and cohesion issues.",
|
|
15
|
+
"agent": "Planner",
|
|
16
|
+
"stage": "analysis"
|
|
17
|
+
},
|
|
18
|
+
"identify_debt": {
|
|
19
|
+
"type": "task",
|
|
20
|
+
"name": "Identify Technical Debt",
|
|
21
|
+
"description": "Find code smells, anti-patterns, outdated practices. Document violations of SOLID, DRY, and separation of concerns.",
|
|
22
|
+
"agent": "Planner",
|
|
23
|
+
"stage": "analysis"
|
|
24
|
+
},
|
|
25
|
+
"classify_components": {
|
|
26
|
+
"type": "task",
|
|
27
|
+
"name": "Classify Components",
|
|
28
|
+
"description": "Categorize code into Functional Core (pure logic, no side effects) vs Imperative Shell (I/O, state, external calls).",
|
|
29
|
+
"agent": "Planner",
|
|
30
|
+
"stage": "analysis"
|
|
31
|
+
},
|
|
32
|
+
"design_refactor": {
|
|
33
|
+
"type": "task",
|
|
34
|
+
"name": "Design Refactor Plan",
|
|
35
|
+
"description": "Create transformation plan: define functional core boundaries, shell interfaces, and migration sequence.",
|
|
36
|
+
"agent": "Planner",
|
|
37
|
+
"stage": "planning"
|
|
38
|
+
},
|
|
39
|
+
"plan_review": {
|
|
40
|
+
"type": "gate",
|
|
41
|
+
"name": "Review Plan",
|
|
42
|
+
"description": "Verify refactor plan maintains behavioral equivalence while achieving architectural goals.",
|
|
43
|
+
"agent": "Reviewer",
|
|
44
|
+
"stage": "planning",
|
|
45
|
+
"maxRetries": 2,
|
|
46
|
+
"config": {
|
|
47
|
+
"scrutinyLevel": 3
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"extract_core": {
|
|
51
|
+
"type": "task",
|
|
52
|
+
"name": "Extract Functional Core",
|
|
53
|
+
"description": "Refactor pure business logic into functional core: no side effects, deterministic, testable in isolation.",
|
|
54
|
+
"agent": "Developer",
|
|
55
|
+
"stage": "development"
|
|
56
|
+
},
|
|
57
|
+
"isolate_shell": {
|
|
58
|
+
"type": "task",
|
|
59
|
+
"name": "Isolate Imperative Shell",
|
|
60
|
+
"description": "Wrap side effects (I/O, state, external services) in thin imperative shell that coordinates functional core.",
|
|
61
|
+
"agent": "Developer",
|
|
62
|
+
"stage": "development"
|
|
63
|
+
},
|
|
64
|
+
"write_tests": {
|
|
65
|
+
"type": "task",
|
|
66
|
+
"name": "Write Tests",
|
|
67
|
+
"description": "Add tests verifying behavioral equivalence. Unit tests for functional core, integration tests for shell.",
|
|
68
|
+
"agent": "Tester",
|
|
69
|
+
"stage": "development"
|
|
70
|
+
},
|
|
71
|
+
"run_tests": {
|
|
72
|
+
"type": "gate",
|
|
73
|
+
"name": "Run Tests",
|
|
74
|
+
"description": "Execute test suite. Verify refactored code produces identical behavior to original.",
|
|
75
|
+
"agent": "Tester",
|
|
76
|
+
"stage": "verification",
|
|
77
|
+
"maxRetries": 3
|
|
78
|
+
},
|
|
79
|
+
"code_review": {
|
|
80
|
+
"type": "gate",
|
|
81
|
+
"name": "Code Review",
|
|
82
|
+
"description": "Review architecture: clean functional/shell separation, no hidden side effects in core, shell is minimal.",
|
|
83
|
+
"agent": "Reviewer",
|
|
84
|
+
"stage": "verification",
|
|
85
|
+
"maxRetries": 2,
|
|
86
|
+
"config": {
|
|
87
|
+
"scrutinyLevel": 3
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"lint_format": {
|
|
91
|
+
"type": "gate",
|
|
92
|
+
"name": "Lint & Format",
|
|
93
|
+
"description": "Run lint and format checks. Auto-fix issues where possible.",
|
|
94
|
+
"agent": "Developer",
|
|
95
|
+
"stage": "delivery",
|
|
96
|
+
"maxRetries": 3
|
|
97
|
+
},
|
|
98
|
+
"commit": {
|
|
99
|
+
"type": "task",
|
|
100
|
+
"name": "Commit Changes",
|
|
101
|
+
"description": "Commit all changes with a descriptive message summarizing the refactoring",
|
|
102
|
+
"agent": "Developer",
|
|
103
|
+
"stage": "delivery"
|
|
104
|
+
},
|
|
105
|
+
"end_success": {
|
|
106
|
+
"type": "end",
|
|
107
|
+
"result": "success",
|
|
108
|
+
"name": "Complete",
|
|
109
|
+
"description": "Refactoring completed successfully"
|
|
110
|
+
},
|
|
111
|
+
"hitl_analysis_failed": {
|
|
112
|
+
"type": "end",
|
|
113
|
+
"result": "blocked",
|
|
114
|
+
"escalation": "hitl",
|
|
115
|
+
"name": "Analysis Blocked",
|
|
116
|
+
"description": "Analysis or planning needs human guidance"
|
|
117
|
+
},
|
|
118
|
+
"hitl_dev_failed": {
|
|
119
|
+
"type": "end",
|
|
120
|
+
"result": "blocked",
|
|
121
|
+
"escalation": "hitl",
|
|
122
|
+
"name": "Development Blocked",
|
|
123
|
+
"description": "Development or verification needs human intervention"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"edges": [
|
|
127
|
+
{
|
|
128
|
+
"from": "start",
|
|
129
|
+
"to": "analyze_structure"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"from": "analyze_structure",
|
|
133
|
+
"to": "identify_debt"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"from": "identify_debt",
|
|
137
|
+
"to": "classify_components"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"from": "classify_components",
|
|
141
|
+
"to": "design_refactor"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"from": "design_refactor",
|
|
145
|
+
"to": "plan_review"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"from": "plan_review",
|
|
149
|
+
"to": "design_refactor",
|
|
150
|
+
"on": "failed",
|
|
151
|
+
"label": "Revise plan based on feedback"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"from": "plan_review",
|
|
155
|
+
"to": "hitl_analysis_failed",
|
|
156
|
+
"on": "failed",
|
|
157
|
+
"label": "Planning exhausted retries"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"from": "plan_review",
|
|
161
|
+
"to": "extract_core",
|
|
162
|
+
"on": "passed",
|
|
163
|
+
"label": "Plan approved, begin refactoring"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"from": "extract_core",
|
|
167
|
+
"to": "isolate_shell"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"from": "isolate_shell",
|
|
171
|
+
"to": "write_tests"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"from": "write_tests",
|
|
175
|
+
"to": "run_tests"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"from": "run_tests",
|
|
179
|
+
"to": "extract_core",
|
|
180
|
+
"on": "failed",
|
|
181
|
+
"label": "Fix failing tests"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"from": "run_tests",
|
|
185
|
+
"to": "hitl_dev_failed",
|
|
186
|
+
"on": "failed",
|
|
187
|
+
"label": "Tests keep failing"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"from": "run_tests",
|
|
191
|
+
"to": "code_review",
|
|
192
|
+
"on": "passed",
|
|
193
|
+
"label": "Tests pass, ready for review"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"from": "code_review",
|
|
197
|
+
"to": "extract_core",
|
|
198
|
+
"on": "failed",
|
|
199
|
+
"label": "Address review feedback"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"from": "code_review",
|
|
203
|
+
"to": "hitl_dev_failed",
|
|
204
|
+
"on": "failed",
|
|
205
|
+
"label": "Review issues persist"
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"from": "code_review",
|
|
209
|
+
"to": "lint_format",
|
|
210
|
+
"on": "passed",
|
|
211
|
+
"label": "Code approved, run lint checks"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"from": "lint_format",
|
|
215
|
+
"to": "commit",
|
|
216
|
+
"on": "passed",
|
|
217
|
+
"label": "Lint passes, commit changes"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"from": "lint_format",
|
|
221
|
+
"to": "extract_core",
|
|
222
|
+
"on": "failed",
|
|
223
|
+
"label": "Fix lint/format issues"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"from": "lint_format",
|
|
227
|
+
"to": "hitl_dev_failed",
|
|
228
|
+
"on": "failed",
|
|
229
|
+
"label": "Lint issues persist"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"from": "commit",
|
|
233
|
+
"to": "end_success"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"from": "hitl_analysis_failed",
|
|
237
|
+
"to": "design_refactor",
|
|
238
|
+
"on": "passed",
|
|
239
|
+
"label": "Human resolved analysis issue, resume"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"from": "hitl_dev_failed",
|
|
243
|
+
"to": "extract_core",
|
|
244
|
+
"on": "passed",
|
|
245
|
+
"label": "Human resolved development issue, resume"
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
@@ -236,6 +236,24 @@
|
|
|
236
236
|
{
|
|
237
237
|
"from": "commit",
|
|
238
238
|
"to": "end_success"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"from": "hitl_ir_failed",
|
|
242
|
+
"to": "ir_component_tree",
|
|
243
|
+
"on": "passed",
|
|
244
|
+
"label": "Human resolved IR issue, resume"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"from": "hitl_build_failed",
|
|
248
|
+
"to": "uiRebuild_build",
|
|
249
|
+
"on": "passed",
|
|
250
|
+
"label": "Human resolved build issue, resume"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"from": "hitl_final_failed",
|
|
254
|
+
"to": "uiRebuild_build",
|
|
255
|
+
"on": "passed",
|
|
256
|
+
"label": "Human resolved final review issue, resume"
|
|
239
257
|
}
|
|
240
258
|
]
|
|
241
259
|
}
|
package/copier.js
CHANGED
|
@@ -12,78 +12,41 @@
|
|
|
12
12
|
export function generateFlowReadme() {
|
|
13
13
|
return `# Flow Plugin
|
|
14
14
|
|
|
15
|
-
DAG-based workflow orchestration for
|
|
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.
|
|
15
|
+
DAG-based workflow orchestration for AI agents.
|
|
20
16
|
|
|
21
17
|
## Quick Start
|
|
22
18
|
|
|
23
|
-
Workflows work immediately from the built-in catalog - no setup required:
|
|
24
|
-
|
|
25
19
|
\`\`\`bash
|
|
26
|
-
#
|
|
27
|
-
/flow:
|
|
20
|
+
# Load the orchestrator at session start
|
|
21
|
+
/flow:prime
|
|
28
22
|
|
|
29
|
-
#
|
|
30
|
-
feat
|
|
31
|
-
bug: Fix login error # → bug-fix workflow
|
|
32
|
-
task: Update config file # → quick-task workflow
|
|
23
|
+
# Create a task using a command
|
|
24
|
+
/flow:feat "add user authentication"
|
|
33
25
|
|
|
34
|
-
#
|
|
35
|
-
/flow:
|
|
26
|
+
# Execute all pending tasks
|
|
27
|
+
/flow:go
|
|
36
28
|
\`\`\`
|
|
37
29
|
|
|
38
30
|
## Commands
|
|
39
31
|
|
|
40
|
-
| Command | Description |
|
|
41
|
-
|
|
42
|
-
| \`/flow:
|
|
43
|
-
| \`/flow:
|
|
44
|
-
| \`/flow:task
|
|
45
|
-
| \`/flow:
|
|
46
|
-
| \`/flow:
|
|
47
|
-
| \`/flow:
|
|
48
|
-
| \`/flow:
|
|
49
|
-
| \`/flow:
|
|
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
|
|
32
|
+
| Command | Workflow | Description |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| \`/flow:feat\` | feature-development | New feature with planning + review |
|
|
35
|
+
| \`/flow:bug\` | bug-fix | Bug investigation and fix |
|
|
36
|
+
| \`/flow:task\` | agile-task | General development task |
|
|
37
|
+
| \`/flow:fix\` | quick-task | Quick fix, minimal ceremony |
|
|
38
|
+
| \`/flow:spec\` | test-coverage | Analyze and improve test coverage |
|
|
39
|
+
| \`/flow:ctx\` | context-optimization | Optimize agent context and prompts |
|
|
40
|
+
| \`/flow:ui\` | ui-reconstruction | Reconstruct UI from reference |
|
|
41
|
+
| \`/flow:go\` | _(runs queue)_ | Execute all pending tasks |
|
|
62
42
|
|
|
63
|
-
|
|
43
|
+
Use \`/flow:task-create "description" <workflow-id>\` for workflows without command shortcuts.
|
|
64
44
|
|
|
65
|
-
|
|
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
|
|
45
|
+
## Available Workflows
|
|
80
46
|
|
|
81
|
-
|
|
47
|
+
Workflows are defined in \`.flow/workflows/\`. Edit \`workflow.json\` to customize, then run \`/flow:load\` to reload.
|
|
82
48
|
|
|
83
|
-
|
|
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
|
|
49
|
+
See [Flow Plugin docs](https://github.com/leclabs/agent-toolkit/tree/main/plugins/flow) for the full workflow catalog.
|
|
87
50
|
`;
|
|
88
51
|
}
|
|
89
52
|
|
package/engine.js
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* - Edge to end node = escalation (taken if retries exhausted)
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { existsSync, readFileSync } from "fs";
|
|
16
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
17
|
+
import { join } from "path";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Read and parse a task file
|
|
@@ -52,13 +53,56 @@ export function getTerminalType(node) {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
|
-
*
|
|
56
|
-
* e.g.,
|
|
56
|
+
* Return agent ID as-is from workflow definition.
|
|
57
|
+
* Prefixing (e.g., @flow:) is the caller's responsibility.
|
|
57
58
|
*/
|
|
58
59
|
export function toSubagentRef(agentId) {
|
|
59
60
|
if (!agentId) return null;
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
return agentId;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Workflow emoji mapping for task subjects
|
|
66
|
+
*/
|
|
67
|
+
const WORKFLOW_EMOJIS = {
|
|
68
|
+
"feature-development": "✨",
|
|
69
|
+
"bug-fix": "🐛",
|
|
70
|
+
"agile-task": "📋",
|
|
71
|
+
"context-optimization": "🔧",
|
|
72
|
+
"quick-task": "⚡",
|
|
73
|
+
"ui-reconstruction": "🎨",
|
|
74
|
+
"test-coverage": "🧪",
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build formatted task subject for write-through
|
|
79
|
+
*/
|
|
80
|
+
export function buildTaskSubject(taskId, userDescription, workflowType, stepId, subagent, terminal, maxRetries, retryCount) {
|
|
81
|
+
const emoji = WORKFLOW_EMOJIS[workflowType] || "";
|
|
82
|
+
const line1 = `#${taskId} ${userDescription}${emoji ? ` ${emoji}` : ""}`;
|
|
83
|
+
|
|
84
|
+
let line2;
|
|
85
|
+
if (terminal === "success") {
|
|
86
|
+
line2 = `→ ${workflowType} · completed ✓`;
|
|
87
|
+
} else if (terminal === "hitl" || terminal === "failure") {
|
|
88
|
+
line2 = `→ ${workflowType} · ${stepId} · HITL`;
|
|
89
|
+
} else {
|
|
90
|
+
const agent = subagent ? `(${subagent})` : "(direct)";
|
|
91
|
+
const retries = maxRetries > 0 ? ` · retries: ${retryCount}/${maxRetries}` : "";
|
|
92
|
+
line2 = `→ ${workflowType} · ${stepId} ${agent}${retries}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return `${line1}\n${line2}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Build activeForm for task spinner display
|
|
100
|
+
*/
|
|
101
|
+
export function buildTaskActiveForm(stepName, subagent, terminal) {
|
|
102
|
+
if (terminal === "success") return "Completed";
|
|
103
|
+
if (terminal === "hitl" || terminal === "failure") return "HITL - Needs human help";
|
|
104
|
+
const agent = subagent ? ` (${subagent})` : "";
|
|
105
|
+
return `${stepName}${agent}`;
|
|
62
106
|
}
|
|
63
107
|
|
|
64
108
|
/**
|
|
@@ -68,8 +112,13 @@ export function getBaselineInstructions(stepId, stepName) {
|
|
|
68
112
|
const id = stepId.toLowerCase();
|
|
69
113
|
const name = (stepName || "").toLowerCase();
|
|
70
114
|
|
|
71
|
-
//
|
|
72
|
-
if (id.includes("
|
|
115
|
+
// Review steps (checked early — "plan_review" is a review, not a plan)
|
|
116
|
+
if (id.includes("review")) {
|
|
117
|
+
return "Check for correctness, code quality, and adherence to project standards. Verify the implementation meets requirements.";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Analysis/Requirements steps
|
|
121
|
+
if (id.includes("analyze") || id.includes("analysis") || id.includes("parse") || id.includes("requirements") || name.includes("analyze")) {
|
|
73
122
|
return "Review the task requirements carefully. Identify key constraints, dependencies, and acceptance criteria. Create a clear plan before proceeding.";
|
|
74
123
|
}
|
|
75
124
|
if (id.includes("plan") || id.includes("design") || name.includes("plan")) {
|
|
@@ -87,16 +136,16 @@ export function getBaselineInstructions(stepId, stepName) {
|
|
|
87
136
|
return "Improve code structure without changing behavior. Ensure all tests pass before and after changes.";
|
|
88
137
|
}
|
|
89
138
|
|
|
139
|
+
// Lint/format steps
|
|
140
|
+
if (id.includes("lint") || id.includes("format")) {
|
|
141
|
+
return "Run linting and formatting checks. Auto-fix issues where possible. Flag any issues that require manual attention.";
|
|
142
|
+
}
|
|
143
|
+
|
|
90
144
|
// Testing steps
|
|
91
145
|
if (id.includes("test") || id.includes("verify") || id.includes("validate")) {
|
|
92
146
|
return "Verify the implementation works correctly. Test happy paths, edge cases, and error conditions. Document any issues found.";
|
|
93
147
|
}
|
|
94
148
|
|
|
95
|
-
// Review steps
|
|
96
|
-
if (id.includes("review")) {
|
|
97
|
-
return "Check for correctness, code quality, and adherence to project standards. Verify the implementation meets requirements.";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
149
|
// Documentation steps
|
|
101
150
|
if (id.includes("document") || id.includes("readme")) {
|
|
102
151
|
return "Write clear, concise documentation. Focus on what users need to know, not implementation details.";
|
|
@@ -124,18 +173,30 @@ export function getBaselineInstructions(stepId, stepName) {
|
|
|
124
173
|
return "Complete this step thoroughly. Document your findings and any decisions made.";
|
|
125
174
|
}
|
|
126
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Build context loading instructions from step-level context_files.
|
|
178
|
+
* Returns a markdown section or null if no context declared.
|
|
179
|
+
*/
|
|
180
|
+
export function buildContextInstructions({ contextFiles, projectRoot }) {
|
|
181
|
+
if (!contextFiles?.length || !projectRoot) return null;
|
|
182
|
+
const lines = contextFiles.map((file) => `- Read file: ${join(projectRoot, file)}`);
|
|
183
|
+
return `## Context\n\nBefore beginning, load the following:\n${lines.join("\n")}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
127
186
|
/**
|
|
128
187
|
* Build orchestrator instructions for task creation/update
|
|
129
188
|
* Returns null for terminal nodes (no further work)
|
|
130
189
|
*/
|
|
131
|
-
function buildOrchestratorInstructions(workflowType, stepId, stage, subagent, stepInstructions, description) {
|
|
190
|
+
function buildOrchestratorInstructions(workflowType, stepId, stage, subagent, stepInstructions, description, contextBlock) {
|
|
132
191
|
if (!stepInstructions) return null; // Terminal nodes have no instructions
|
|
133
192
|
|
|
134
193
|
const delegationPrefix = subagent ? `Invoke ${subagent} to complete the following task: ` : "";
|
|
135
194
|
|
|
136
|
-
|
|
195
|
+
let result = `${delegationPrefix}${stepInstructions.guidance}
|
|
137
196
|
|
|
138
197
|
${description || "{task description}"}`;
|
|
198
|
+
if (contextBlock) result += `\n\n${contextBlock}`;
|
|
199
|
+
return result;
|
|
139
200
|
}
|
|
140
201
|
|
|
141
202
|
/**
|
|
@@ -149,7 +210,9 @@ function buildNavigateResponse(
|
|
|
149
210
|
action,
|
|
150
211
|
retriesIncremented = false,
|
|
151
212
|
retryCount = 0,
|
|
152
|
-
description = null
|
|
213
|
+
description = null,
|
|
214
|
+
resetRetryCount = false,
|
|
215
|
+
projectRoot = null
|
|
153
216
|
) {
|
|
154
217
|
const stage = stepDef.stage || null;
|
|
155
218
|
const subagent = stepDef.agent ? toSubagentRef(stepDef.agent) : null;
|
|
@@ -164,16 +227,27 @@ function buildNavigateResponse(
|
|
|
164
227
|
guidance: stepDef.instructions || getBaselineInstructions(stepId, stepDef.name),
|
|
165
228
|
};
|
|
166
229
|
|
|
230
|
+
// Build context block from step-level context_files
|
|
231
|
+
const contextBlock = isTerminal
|
|
232
|
+
? null
|
|
233
|
+
: buildContextInstructions({ contextFiles: stepDef.context_files, projectRoot });
|
|
234
|
+
|
|
167
235
|
// Build orchestrator instructions for all non-terminal actions
|
|
168
236
|
const orchestratorInstructions = isTerminal
|
|
169
237
|
? null
|
|
170
|
-
: buildOrchestratorInstructions(workflowType, stepId, stage, subagent, stepInstructions, description);
|
|
238
|
+
: buildOrchestratorInstructions(workflowType, stepId, stage, subagent, stepInstructions, description, contextBlock);
|
|
171
239
|
|
|
172
240
|
// Build metadata for task storage
|
|
241
|
+
// Increment on retry, reset on start or explicit forward progress (conditional advance),
|
|
242
|
+
// preserve on unconditional advances within retry loops and escalations
|
|
173
243
|
const metadata = {
|
|
174
244
|
workflowType,
|
|
175
245
|
currentStep: stepId,
|
|
176
|
-
retryCount: retriesIncremented
|
|
246
|
+
retryCount: retriesIncremented
|
|
247
|
+
? retryCount + 1
|
|
248
|
+
: action === "start" || resetRetryCount
|
|
249
|
+
? 0
|
|
250
|
+
: retryCount,
|
|
177
251
|
};
|
|
178
252
|
|
|
179
253
|
return {
|
|
@@ -184,6 +258,7 @@ function buildNavigateResponse(
|
|
|
184
258
|
terminal: getTerminalType(stepDef),
|
|
185
259
|
action,
|
|
186
260
|
retriesIncremented,
|
|
261
|
+
maxRetries: stepDef.maxRetries || 0,
|
|
187
262
|
orchestratorInstructions,
|
|
188
263
|
metadata,
|
|
189
264
|
};
|
|
@@ -354,7 +429,7 @@ export class WorkflowEngine {
|
|
|
354
429
|
* @param {string} [options.description] - User's task description
|
|
355
430
|
* @returns {Object} Navigation response with currentStep, stepInstructions, terminal, action, metadata, etc.
|
|
356
431
|
*/
|
|
357
|
-
navigate({ taskFilePath, workflowType, result, description } = {}) {
|
|
432
|
+
navigate({ taskFilePath, workflowType, result, description, projectRoot } = {}) {
|
|
358
433
|
let currentStep = null;
|
|
359
434
|
let retryCount = 0;
|
|
360
435
|
|
|
@@ -414,7 +489,7 @@ export class WorkflowEngine {
|
|
|
414
489
|
throw new Error(`First step '${firstEdge.to}' not found in workflow`);
|
|
415
490
|
}
|
|
416
491
|
|
|
417
|
-
return buildNavigateResponse(workflowType, firstEdge.to, firstStepDef, "start", false, 0, description);
|
|
492
|
+
return buildNavigateResponse(workflowType, firstEdge.to, firstStepDef, "start", false, 0, description, false, projectRoot);
|
|
418
493
|
}
|
|
419
494
|
|
|
420
495
|
// Case 2: currentStep but no result - return current state
|
|
@@ -424,7 +499,7 @@ export class WorkflowEngine {
|
|
|
424
499
|
throw new Error(`Step '${currentStep}' not found in workflow '${workflowType}'`);
|
|
425
500
|
}
|
|
426
501
|
|
|
427
|
-
return buildNavigateResponse(workflowType, currentStep, stepDef, "current", false, retryCount, description);
|
|
502
|
+
return buildNavigateResponse(workflowType, currentStep, stepDef, "current", false, retryCount, description, false, projectRoot);
|
|
428
503
|
}
|
|
429
504
|
|
|
430
505
|
// Case 3: currentStep and result - advance to next step
|
|
@@ -444,9 +519,13 @@ export class WorkflowEngine {
|
|
|
444
519
|
}
|
|
445
520
|
|
|
446
521
|
// Determine action and whether retries incremented
|
|
522
|
+
const currentStepDef = nodes[currentStep];
|
|
523
|
+
const isHitlResume = getTerminalType(currentStepDef) === "hitl";
|
|
447
524
|
const isRetry = evaluation.action === "retry";
|
|
448
525
|
let action;
|
|
449
|
-
if (
|
|
526
|
+
if (isHitlResume) {
|
|
527
|
+
action = "advance"; // Human fixed it → fresh advance, retryCount resets
|
|
528
|
+
} else if (isRetry) {
|
|
450
529
|
action = "retry";
|
|
451
530
|
} else if (getTerminalType(nextStepDef) === "hitl") {
|
|
452
531
|
action = "escalate";
|
|
@@ -454,14 +533,44 @@ export class WorkflowEngine {
|
|
|
454
533
|
action = "advance";
|
|
455
534
|
}
|
|
456
535
|
|
|
457
|
-
|
|
536
|
+
// Only reset retryCount on genuine forward progress (conditional edge like on:"passed")
|
|
537
|
+
// Unconditional advances within retry loops (e.g., work → gate) preserve the count
|
|
538
|
+
const resetRetryCount = action === "advance" && evaluation.action === "conditional";
|
|
539
|
+
|
|
540
|
+
const response = buildNavigateResponse(
|
|
458
541
|
workflowType,
|
|
459
542
|
evaluation.nextStep,
|
|
460
543
|
nextStepDef,
|
|
461
544
|
action,
|
|
462
545
|
isRetry,
|
|
463
546
|
retryCount,
|
|
464
|
-
description
|
|
547
|
+
description,
|
|
548
|
+
resetRetryCount,
|
|
549
|
+
projectRoot
|
|
465
550
|
);
|
|
551
|
+
|
|
552
|
+
// Write-through: persist state transition and presentation to task file
|
|
553
|
+
if (taskFilePath) {
|
|
554
|
+
const task = readTaskFile(taskFilePath);
|
|
555
|
+
if (task) {
|
|
556
|
+
const userDesc = task.metadata?.userDescription || "";
|
|
557
|
+
task.metadata = { ...task.metadata, ...response.metadata };
|
|
558
|
+
task.subject = buildTaskSubject(
|
|
559
|
+
task.id, userDesc, response.metadata.workflowType,
|
|
560
|
+
response.currentStep, response.subagent, response.terminal,
|
|
561
|
+
response.maxRetries, response.metadata.retryCount
|
|
562
|
+
);
|
|
563
|
+
task.activeForm = buildTaskActiveForm(
|
|
564
|
+
response.stepInstructions?.name || response.currentStep,
|
|
565
|
+
response.subagent, response.terminal
|
|
566
|
+
);
|
|
567
|
+
if (response.orchestratorInstructions) {
|
|
568
|
+
task.description = response.orchestratorInstructions;
|
|
569
|
+
}
|
|
570
|
+
writeFileSync(taskFilePath, JSON.stringify(task, null, 2));
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return response;
|
|
466
575
|
}
|
|
467
576
|
}
|