@jmkim85/dev-flow-mcp
Version:
MCP-based Dev Flow - AI-powered development workflow management with 13 essential tools for TDD and context management
197 lines (190 loc) β’ 8.43 kB
JavaScript
/**
* Workflow Tool Handlers Module
* Implements workflow management MCP tool handlers for Dev Flow
*/
import { stateManager, tddValidator, rewardHackingDetector, unifiedContextManager, simpleWorkflowManager, PROJECT_ROOT } from './globals.js';
import { findStageOutputs, generateMissingFilesFeedback } from './safety-guards.js';
// Workflow management tool handler implementations
export async function loadWorkflow(workflowId) {
try {
let workflow = await simpleWorkflowManager.loadWorkflow(workflowId);
if (!workflow) {
// Create default workflows if none exist
await simpleWorkflowManager.createDefaultWorkflows();
workflow = await simpleWorkflowManager.loadWorkflow(workflowId);
if (!workflow) {
const availableWorkflows = await simpleWorkflowManager.listWorkflows();
return {
content: [{
type: "text",
text: `β Workflow '${workflowId}' not found. Available workflows: ${availableWorkflows.join(', ') || 'none'}`
}]
};
}
}
// πΈ μν¬νλ‘μ° λ‘λ μ±κ³΅ ν State μ λ°μ
const state = await stateManager.getCurrentState();
state.current_workflow = workflowId;
await stateManager.saveState(state);
return {
content: [{
type: "text",
text: `π **Loaded Workflow**: ${workflow.name}
**Description**: ${workflow.description}
**Stages** (${workflow.stages.length} total):
${workflow.stages.map((s, i) => `${i + 1}. **${s.name}**
*Instructions*: ${s.instructions}
${s.entry_criteria ? `*Entry*: ${s.entry_criteria.join(', ')}` : ''}
${s.exit_criteria ? `*Exit*: ${s.exit_criteria.join(', ')}` : ''}`).join('\n\n')}
**Current Workflow Set**: ${workflowId}
Use \`start_task\` with a stage from this workflow, or \`advance_stage\` to move to the next stage.`
}]
};
}
catch (error) {
return {
content: [{
type: "text",
text: `β Error loading workflow: ${error.message}`
}]
};
}
}
export async function advanceStage(summary) {
const state = await stateManager.getCurrentState();
if (!state.current_task) {
return {
content: [{
type: "text",
text: "β οΈ **No Active Task**\n\nStart a task first using `get_next_task` and `start_task`."
}]
};
}
const workflowId = state.current_workflow || "tdd_strict";
const workflow = await simpleWorkflowManager.loadWorkflow(workflowId);
if (!workflow) {
return {
content: [{
type: "text",
text: `β **Workflow Not Found**\n\nWorkflow '${workflowId}' not found. Load a workflow first using \`load_workflow\`.`
}]
};
}
const nextStage = simpleWorkflowManager.getNextStage(workflow, state.current_stage);
if (!nextStage) {
return {
content: [{
type: "text",
text: `π© **Final Stage Reached**\n\n'${state.current_stage}' is the final stage in workflow '${workflowId}'. Use \`complete_task\` to finish.`
}]
};
}
// Get current task for validation
const task = await stateManager.getTask(state.current_task);
if (!task) {
return {
content: [{
type: "text",
text: `β **Task Not Found**\n\nCurrent task '${state.current_task}' not found.`
}]
};
}
// NEW: Workflow Entry/Exit Criteria Validation
const exitCheck = await simpleWorkflowManager.checkExitCriteria(workflow, state.current_stage, PROJECT_ROOT);
if (!exitCheck.met) {
return {
content: [{
type: "text",
text: `β **Exit Criteria Not Met**\n\nCannot leave '${state.current_stage}' stage. The following criteria are not satisfied:\n\n${exitCheck.unmetCriteria.map(c => `β’ ${c}`).join('\n')}\n\n**Details**: ${exitCheck.details}\n\nComplete these requirements before advancing.`
}]
};
}
const entryCheck = await simpleWorkflowManager.checkEntryCriteria(workflow, nextStage, PROJECT_ROOT);
if (!entryCheck.met) {
return {
content: [{
type: "text",
text: `β **Entry Criteria Not Met**\n\nCannot enter '${nextStage}' stage. The following criteria are not satisfied:\n\n${entryCheck.unmetCriteria.map(c => `β’ ${c}`).join('\n')}\n\n**Details**: ${entryCheck.details}\n\nEnsure prerequisites are met before advancing.`
}]
};
}
// TDD validation (legacy - now supplemented by workflow criteria)
const validation = await tddValidator.validateStageTransition(state.current_stage, nextStage);
if (!validation.allowed) {
return {
content: [{
type: "text",
text: `β **TDD Validation Failed**\n\n${validation.reason}\n\nComplete the current stage requirements before advancing.`
}]
};
}
// Reward hacking detection
try {
// Check for actual outputs created in task-specific directory
const outputsCreated = await findStageOutputs(state.current_stage, state.current_task, PROJECT_ROOT);
const hackingCheck = await rewardHackingDetector.checkStageAdvancement(state.current_stage, nextStage, {
stage_iterations: { [state.current_stage]: 1 },
time_in_stage: 5, // Assume minimum time spent
outputs_created: outputsCreated
});
if (!hackingCheck.allowed) {
// μμ μ₯μΉ 3-4: λλ½ νμΌμ λν ꡬ체μ νΌλλ°± μ 곡
const reason = hackingCheck.reason || 'Unknown reason';
let feedbackMessage = `π« **Stage Advancement Blocked**\n\n${reason}`;
if (reason.includes('Missing required outputs')) {
const missingFilesFeedback = await generateMissingFilesFeedback(state.current_stage, state.current_task, PROJECT_ROOT);
if (missingFilesFeedback) {
feedbackMessage += `\n\n${missingFilesFeedback}`;
}
}
else {
feedbackMessage += '\n\nSpend more time on the current stage before advancing.';
}
return {
content: [{
type: "text",
text: feedbackMessage
}]
};
}
}
catch (error) {
console.error('Warning: Could not run reward hacking check:', error);
}
// Update state and track stage iterations
const previousStage = state.current_stage;
state.current_stage = nextStage; // Type assertion for enum
// Reset iteration counter for the new stage and increment for current stage
await stateManager.resetStageIteration(nextStage);
const iterationResult = await stateManager.incrementStageIteration(previousStage);
await stateManager.saveState(state);
// Check for excessive iterations warning
let iterationWarning = '';
if (iterationResult.warning) {
iterationWarning = `\n\n${iterationResult.warning}`;
}
// Update context frame
try {
await unifiedContextManager.updateFrame({
stage: nextStage,
summary: summary || `Advanced from ${previousStage} to ${nextStage}`
});
await unifiedContextManager.addFact(`Advanced to stage "${nextStage}" in workflow "${workflowId}"`, false);
}
catch (error) {
console.error('Warning: Could not update context frame:', error);
}
return {
content: [{
type: "text",
text: `β
**Stage Advanced Successfully**
**Previous Stage**: ${previousStage} (${iterationResult.count} iterations)
**Current Stage**: ${nextStage}
**Task**: ${task.title}
**Workflow**: ${workflowId}
${summary ? `**Summary**: ${summary}` : ''}
**Next Steps**: Follow the instructions for the '${nextStage}' stage in your workflow.${iterationWarning}`
}]
};
}
//# sourceMappingURL=workflow-tool-handlers.js.map