@codemcp/workflows 3.1.21
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/.turbo/turbo-build.log +4 -0
- package/.vibe/conversation-state.sqlite +0 -0
- package/LICENSE +674 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/notification-service.d.ts +14 -0
- package/dist/notification-service.d.ts.map +1 -0
- package/dist/notification-service.js +18 -0
- package/dist/notification-service.js.map +1 -0
- package/dist/resource-handlers/conversation-state.d.ts +15 -0
- package/dist/resource-handlers/conversation-state.d.ts.map +1 -0
- package/dist/resource-handlers/conversation-state.js +40 -0
- package/dist/resource-handlers/conversation-state.js.map +1 -0
- package/dist/resource-handlers/development-plan.d.ts +14 -0
- package/dist/resource-handlers/development-plan.d.ts.map +1 -0
- package/dist/resource-handlers/development-plan.js +31 -0
- package/dist/resource-handlers/development-plan.js.map +1 -0
- package/dist/resource-handlers/index.d.ts +24 -0
- package/dist/resource-handlers/index.d.ts.map +1 -0
- package/dist/resource-handlers/index.js +62 -0
- package/dist/resource-handlers/index.js.map +1 -0
- package/dist/resource-handlers/system-prompt.d.ts +15 -0
- package/dist/resource-handlers/system-prompt.d.ts.map +1 -0
- package/dist/resource-handlers/system-prompt.js +40 -0
- package/dist/resource-handlers/system-prompt.js.map +1 -0
- package/dist/resource-handlers/workflow-resource.d.ts +15 -0
- package/dist/resource-handlers/workflow-resource.d.ts.map +1 -0
- package/dist/resource-handlers/workflow-resource.js +85 -0
- package/dist/resource-handlers/workflow-resource.js.map +1 -0
- package/dist/response-renderer.d.ts +30 -0
- package/dist/response-renderer.d.ts.map +1 -0
- package/dist/response-renderer.js +94 -0
- package/dist/response-renderer.js.map +1 -0
- package/dist/server-config.d.ts +34 -0
- package/dist/server-config.d.ts.map +1 -0
- package/dist/server-config.js +486 -0
- package/dist/server-config.js.map +1 -0
- package/dist/server-helpers.d.ts +62 -0
- package/dist/server-helpers.d.ts.map +1 -0
- package/dist/server-helpers.js +156 -0
- package/dist/server-helpers.js.map +1 -0
- package/dist/server-implementation.d.ts +74 -0
- package/dist/server-implementation.d.ts.map +1 -0
- package/dist/server-implementation.js +201 -0
- package/dist/server-implementation.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +5 -0
- package/dist/server.js.map +1 -0
- package/dist/tool-handlers/base-tool-handler.d.ts +50 -0
- package/dist/tool-handlers/base-tool-handler.d.ts.map +1 -0
- package/dist/tool-handlers/base-tool-handler.js +74 -0
- package/dist/tool-handlers/base-tool-handler.js.map +1 -0
- package/dist/tool-handlers/conduct-review.d.ts +49 -0
- package/dist/tool-handlers/conduct-review.d.ts.map +1 -0
- package/dist/tool-handlers/conduct-review.js +105 -0
- package/dist/tool-handlers/conduct-review.js.map +1 -0
- package/dist/tool-handlers/get-tool-info.d.ts +76 -0
- package/dist/tool-handlers/get-tool-info.d.ts.map +1 -0
- package/dist/tool-handlers/get-tool-info.js +168 -0
- package/dist/tool-handlers/get-tool-info.js.map +1 -0
- package/dist/tool-handlers/index.d.ts +42 -0
- package/dist/tool-handlers/index.d.ts.map +1 -0
- package/dist/tool-handlers/index.js +74 -0
- package/dist/tool-handlers/index.js.map +1 -0
- package/dist/tool-handlers/install-workflow.d.ts +48 -0
- package/dist/tool-handlers/install-workflow.d.ts.map +1 -0
- package/dist/tool-handlers/install-workflow.js +131 -0
- package/dist/tool-handlers/install-workflow.js.map +1 -0
- package/dist/tool-handlers/list-workflows.d.ts +47 -0
- package/dist/tool-handlers/list-workflows.d.ts.map +1 -0
- package/dist/tool-handlers/list-workflows.js +58 -0
- package/dist/tool-handlers/list-workflows.js.map +1 -0
- package/dist/tool-handlers/no-idea.d.ts +41 -0
- package/dist/tool-handlers/no-idea.d.ts.map +1 -0
- package/dist/tool-handlers/no-idea.js +29 -0
- package/dist/tool-handlers/no-idea.js.map +1 -0
- package/dist/tool-handlers/proceed-to-phase.d.ts +39 -0
- package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -0
- package/dist/tool-handlers/proceed-to-phase.js +109 -0
- package/dist/tool-handlers/proceed-to-phase.js.map +1 -0
- package/dist/tool-handlers/reset-development.d.ts +31 -0
- package/dist/tool-handlers/reset-development.d.ts.map +1 -0
- package/dist/tool-handlers/reset-development.js +48 -0
- package/dist/tool-handlers/reset-development.js.map +1 -0
- package/dist/tool-handlers/resume-workflow.d.ts +88 -0
- package/dist/tool-handlers/resume-workflow.d.ts.map +1 -0
- package/dist/tool-handlers/resume-workflow.js +213 -0
- package/dist/tool-handlers/resume-workflow.js.map +1 -0
- package/dist/tool-handlers/setup-project-docs.d.ts +36 -0
- package/dist/tool-handlers/setup-project-docs.d.ts.map +1 -0
- package/dist/tool-handlers/setup-project-docs.js +136 -0
- package/dist/tool-handlers/setup-project-docs.js.map +1 -0
- package/dist/tool-handlers/start-development.d.ts +82 -0
- package/dist/tool-handlers/start-development.d.ts.map +1 -0
- package/dist/tool-handlers/start-development.js +448 -0
- package/dist/tool-handlers/start-development.js.map +1 -0
- package/dist/tool-handlers/whats-next.d.ts +42 -0
- package/dist/tool-handlers/whats-next.d.ts.map +1 -0
- package/dist/tool-handlers/whats-next.js +118 -0
- package/dist/tool-handlers/whats-next.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +29 -0
- package/src/index.ts +93 -0
- package/src/notification-service.ts +23 -0
- package/src/resource-handlers/conversation-state.ts +55 -0
- package/src/resource-handlers/development-plan.ts +48 -0
- package/src/resource-handlers/index.ts +73 -0
- package/src/resource-handlers/system-prompt.ts +55 -0
- package/src/resource-handlers/workflow-resource.ts +126 -0
- package/src/response-renderer.ts +116 -0
- package/src/server-config.ts +744 -0
- package/src/server-helpers.ts +225 -0
- package/src/server-implementation.ts +277 -0
- package/src/server.ts +9 -0
- package/src/tool-handlers/base-tool-handler.ts +141 -0
- package/src/tool-handlers/conduct-review.ts +191 -0
- package/src/tool-handlers/get-tool-info.ts +274 -0
- package/src/tool-handlers/index.ts +117 -0
- package/src/tool-handlers/install-workflow.ts +185 -0
- package/src/tool-handlers/list-workflows.ts +94 -0
- package/src/tool-handlers/no-idea.ts +47 -0
- package/src/tool-handlers/proceed-to-phase.ts +205 -0
- package/src/tool-handlers/reset-development.ts +90 -0
- package/src/tool-handlers/resume-workflow.ts +380 -0
- package/src/tool-handlers/setup-project-docs.ts +226 -0
- package/src/tool-handlers/start-development.ts +685 -0
- package/src/tool-handlers/whats-next.ts +235 -0
- package/src/types.ts +130 -0
- package/test/e2e/core-functionality.test.ts +176 -0
- package/test/e2e/mcp-contract.test.ts +540 -0
- package/test/e2e/plan-management.test.ts +331 -0
- package/test/e2e/state-management.test.ts +392 -0
- package/test/e2e/workflow-integration.test.ts +506 -0
- package/test/unit/commit-behaviour-interface.test.ts +244 -0
- package/test/unit/conduct-review.test.ts +151 -0
- package/test/unit/reset-functionality.test.ts +72 -0
- package/test/unit/resume-workflow.test.ts +192 -0
- package/test/unit/server-tools.test.ts +311 -0
- package/test/unit/setup-project-docs-handler.test.ts +267 -0
- package/test/unit/start-development-artifact-detection.test.ts +387 -0
- package/test/unit/start-development-gitignore.test.ts +178 -0
- package/test/unit/system-prompt-resource.test.ts +101 -0
- package/test/unit/tool-handlers/no-idea.test.ts +40 -0
- package/test/utils/e2e-test-setup.ts +453 -0
- package/test/utils/run-server-in-dir.sh +27 -0
- package/test/utils/temp-files.ts +308 -0
- package/test/utils/test-access.ts +79 -0
- package/test/utils/test-helpers.ts +286 -0
- package/test/utils/test-setup.ts +78 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +12 -0
- package/vitest.config.ts +17 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResumeWorkflow Tool Handler
|
|
3
|
+
*
|
|
4
|
+
* Handles resuming development workflow after conversation compression.
|
|
5
|
+
* Returns comprehensive project context, current state, system prompt instructions,
|
|
6
|
+
* and next steps to seamlessly continue development.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ConversationRequiredToolHandler } from './base-tool-handler.js';
|
|
10
|
+
import { generateSystemPrompt } from '@codemcp/workflows-core';
|
|
11
|
+
import type { YamlStateMachine, YamlState } from '@codemcp/workflows-core';
|
|
12
|
+
import { ServerContext } from '../types.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Arguments for the resume_workflow tool
|
|
16
|
+
*/
|
|
17
|
+
export interface ResumeWorkflowArgs {
|
|
18
|
+
include_system_prompt?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Plan file analysis result
|
|
23
|
+
*/
|
|
24
|
+
interface PlanAnalysis {
|
|
25
|
+
sections: string[];
|
|
26
|
+
tasks_completed: number;
|
|
27
|
+
tasks_total: number;
|
|
28
|
+
key_decisions: string[];
|
|
29
|
+
recent_updates: string[];
|
|
30
|
+
active_tasks?: string[];
|
|
31
|
+
completed_tasks?: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Conversation context for resume workflow
|
|
36
|
+
*/
|
|
37
|
+
interface ConversationContext {
|
|
38
|
+
conversationId: string;
|
|
39
|
+
currentPhase: string;
|
|
40
|
+
projectPath: string;
|
|
41
|
+
workflowName: string;
|
|
42
|
+
gitBranch: string;
|
|
43
|
+
planFilePath: string;
|
|
44
|
+
current_phase?: string;
|
|
45
|
+
workflow_name?: string;
|
|
46
|
+
project_context?: string;
|
|
47
|
+
recent_activity?: string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Recommendations for resuming workflow
|
|
52
|
+
*/
|
|
53
|
+
interface WorkflowRecommendations {
|
|
54
|
+
immediate_actions: string[];
|
|
55
|
+
phase_guidance: string;
|
|
56
|
+
potential_issues: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Response from the resume_workflow tool
|
|
61
|
+
*/
|
|
62
|
+
export interface ResumeWorkflowResult {
|
|
63
|
+
workflow_status: {
|
|
64
|
+
conversation_id: string;
|
|
65
|
+
current_phase: string;
|
|
66
|
+
project_path: string;
|
|
67
|
+
git_branch: string;
|
|
68
|
+
state_machine: YamlStateMachine;
|
|
69
|
+
};
|
|
70
|
+
plan_status: {
|
|
71
|
+
exists: boolean;
|
|
72
|
+
path: string;
|
|
73
|
+
analysis?: PlanAnalysis | null;
|
|
74
|
+
};
|
|
75
|
+
system_prompt?: string | null;
|
|
76
|
+
recommendations: {
|
|
77
|
+
immediate_actions: string[];
|
|
78
|
+
phase_guidance: string;
|
|
79
|
+
potential_issues: string[];
|
|
80
|
+
};
|
|
81
|
+
generated_at: string;
|
|
82
|
+
tool_version: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* ResumeWorkflow tool handler implementation
|
|
87
|
+
*/
|
|
88
|
+
export class ResumeWorkflowHandler extends ConversationRequiredToolHandler<
|
|
89
|
+
ResumeWorkflowArgs,
|
|
90
|
+
ResumeWorkflowResult
|
|
91
|
+
> {
|
|
92
|
+
protected async executeWithConversation(
|
|
93
|
+
args: ResumeWorkflowArgs,
|
|
94
|
+
context: ServerContext,
|
|
95
|
+
conversationContext: ConversationContext
|
|
96
|
+
): Promise<ResumeWorkflowResult> {
|
|
97
|
+
const includeSystemPrompt = args.include_system_prompt !== false; // Default to true
|
|
98
|
+
|
|
99
|
+
this.logger.debug('Processing resume_workflow request', {
|
|
100
|
+
conversationId: conversationContext.conversationId,
|
|
101
|
+
includeSystemPrompt,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Get plan file information
|
|
105
|
+
const planInfo = await context.planManager.getPlanFileInfo(
|
|
106
|
+
conversationContext.planFilePath
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Analyze plan file content for key information
|
|
110
|
+
const planAnalysis =
|
|
111
|
+
planInfo.exists && planInfo.content
|
|
112
|
+
? this.analyzePlanFile(planInfo.content)
|
|
113
|
+
: null;
|
|
114
|
+
|
|
115
|
+
// Get current state machine information
|
|
116
|
+
const stateMachineInfo = await this.getStateMachineInfo(
|
|
117
|
+
context,
|
|
118
|
+
conversationContext.projectPath,
|
|
119
|
+
conversationContext.workflowName
|
|
120
|
+
);
|
|
121
|
+
const stateMachine = context.transitionEngine.getStateMachine(
|
|
122
|
+
conversationContext.projectPath,
|
|
123
|
+
conversationContext.workflowName
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Generate system prompt if requested
|
|
127
|
+
const systemPrompt = includeSystemPrompt
|
|
128
|
+
? generateSystemPrompt(stateMachine)
|
|
129
|
+
: null;
|
|
130
|
+
|
|
131
|
+
// Build comprehensive response
|
|
132
|
+
const response: ResumeWorkflowResult = {
|
|
133
|
+
// Core workflow resumption info
|
|
134
|
+
workflow_status: {
|
|
135
|
+
conversation_id: conversationContext.conversationId,
|
|
136
|
+
current_phase: conversationContext.currentPhase,
|
|
137
|
+
project_path: conversationContext.projectPath,
|
|
138
|
+
git_branch: conversationContext.gitBranch,
|
|
139
|
+
state_machine: stateMachineInfo as YamlStateMachine,
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
// Plan file analysis
|
|
143
|
+
plan_status: {
|
|
144
|
+
exists: planInfo.exists,
|
|
145
|
+
path: conversationContext.planFilePath,
|
|
146
|
+
analysis: planAnalysis,
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// System prompt (if requested)
|
|
150
|
+
system_prompt: systemPrompt,
|
|
151
|
+
|
|
152
|
+
// Next steps and recommendations
|
|
153
|
+
recommendations: this.generateRecommendations(
|
|
154
|
+
conversationContext,
|
|
155
|
+
planAnalysis,
|
|
156
|
+
context
|
|
157
|
+
),
|
|
158
|
+
|
|
159
|
+
// Metadata
|
|
160
|
+
generated_at: new Date().toISOString(),
|
|
161
|
+
tool_version: '1.0.0',
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
this.logger.debug('resume_workflow response generated', {
|
|
165
|
+
conversationId: conversationContext.conversationId,
|
|
166
|
+
phase: conversationContext.currentPhase,
|
|
167
|
+
planExists: planInfo.exists,
|
|
168
|
+
includeSystemPrompt,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return response;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Analyze plan file content to extract key information
|
|
176
|
+
*/
|
|
177
|
+
private analyzePlanFile(content: string): PlanAnalysis {
|
|
178
|
+
const analysis = {
|
|
179
|
+
active_tasks: [] as string[],
|
|
180
|
+
completed_tasks: [] as string[],
|
|
181
|
+
recent_decisions: [] as string[],
|
|
182
|
+
next_steps: [] as string[],
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const lines = content.split('\n');
|
|
186
|
+
let inTaskSection = false;
|
|
187
|
+
let currentSection = '';
|
|
188
|
+
|
|
189
|
+
for (const line of lines) {
|
|
190
|
+
const trimmed = line.trim();
|
|
191
|
+
|
|
192
|
+
// Detect sections
|
|
193
|
+
if (trimmed.startsWith('##')) {
|
|
194
|
+
currentSection = trimmed.toLowerCase();
|
|
195
|
+
inTaskSection =
|
|
196
|
+
currentSection.includes('task') || currentSection.includes('todo');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Extract tasks
|
|
200
|
+
if (inTaskSection) {
|
|
201
|
+
if (trimmed.startsWith('- [x]')) {
|
|
202
|
+
analysis.completed_tasks.push(trimmed.substring(5).trim());
|
|
203
|
+
} else if (trimmed.startsWith('- [ ]')) {
|
|
204
|
+
analysis.active_tasks.push(trimmed.substring(5).trim());
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Extract decisions (look for decision log sections)
|
|
209
|
+
if (currentSection.includes('decision') && trimmed.startsWith('- ')) {
|
|
210
|
+
analysis.recent_decisions.push(trimmed.substring(2).trim());
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
sections: [currentSection],
|
|
216
|
+
tasks_completed: analysis.completed_tasks.length,
|
|
217
|
+
tasks_total:
|
|
218
|
+
analysis.active_tasks.length + analysis.completed_tasks.length,
|
|
219
|
+
key_decisions: analysis.recent_decisions,
|
|
220
|
+
recent_updates: analysis.next_steps,
|
|
221
|
+
active_tasks: analysis.active_tasks,
|
|
222
|
+
completed_tasks: analysis.completed_tasks,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get state machine information
|
|
228
|
+
*/
|
|
229
|
+
private async getStateMachineInfo(
|
|
230
|
+
context: ServerContext,
|
|
231
|
+
projectPath: string,
|
|
232
|
+
workflowName?: string
|
|
233
|
+
): Promise<unknown> {
|
|
234
|
+
try {
|
|
235
|
+
// Get the actual state machine for this project
|
|
236
|
+
const stateMachine = context.transitionEngine.getStateMachine(
|
|
237
|
+
projectPath,
|
|
238
|
+
workflowName
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
name: stateMachine.name,
|
|
243
|
+
description: stateMachine.description,
|
|
244
|
+
initial_state: stateMachine.initial_state,
|
|
245
|
+
phases: Object.keys(stateMachine.states),
|
|
246
|
+
phase_descriptions: Object.fromEntries(
|
|
247
|
+
Object.entries(stateMachine.states).map(([phase, definition]) => [
|
|
248
|
+
phase,
|
|
249
|
+
(definition as YamlState).description,
|
|
250
|
+
])
|
|
251
|
+
),
|
|
252
|
+
};
|
|
253
|
+
} catch (error) {
|
|
254
|
+
const err = error as Error;
|
|
255
|
+
this.logger.warn('Could not determine state machine info', {
|
|
256
|
+
error: err.message,
|
|
257
|
+
});
|
|
258
|
+
return {
|
|
259
|
+
name: 'unknown',
|
|
260
|
+
description: 'Could not load workflow',
|
|
261
|
+
initial_state: 'unknown',
|
|
262
|
+
phases: [],
|
|
263
|
+
phase_descriptions: {},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Generate recommendations for next steps based on state machine transitions
|
|
270
|
+
*/
|
|
271
|
+
private generateRecommendations(
|
|
272
|
+
conversationContext: ConversationContext,
|
|
273
|
+
planAnalysis: PlanAnalysis | null,
|
|
274
|
+
context: ServerContext
|
|
275
|
+
): WorkflowRecommendations {
|
|
276
|
+
const recommendations = {
|
|
277
|
+
immediate_actions: [] as string[],
|
|
278
|
+
phase_guidance: '',
|
|
279
|
+
potential_issues: [] as string[],
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
// Get the state machine for this project
|
|
284
|
+
const stateMachine = context.transitionEngine.getStateMachine(
|
|
285
|
+
conversationContext.projectPath,
|
|
286
|
+
conversationContext.workflowName
|
|
287
|
+
);
|
|
288
|
+
const currentPhase = conversationContext.currentPhase;
|
|
289
|
+
const phaseDefinition = stateMachine.states[currentPhase];
|
|
290
|
+
|
|
291
|
+
if (phaseDefinition) {
|
|
292
|
+
// Set phase guidance from state machine description
|
|
293
|
+
recommendations.phase_guidance = `Current phase: ${(phaseDefinition as YamlState).description}`;
|
|
294
|
+
|
|
295
|
+
// Generate transition-based recommendations
|
|
296
|
+
if (
|
|
297
|
+
(phaseDefinition as YamlState).transitions &&
|
|
298
|
+
(phaseDefinition as YamlState).transitions.length > 0
|
|
299
|
+
) {
|
|
300
|
+
recommendations.immediate_actions.push(
|
|
301
|
+
'From here, you can transition to:'
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
for (const transition of phaseDefinition.transitions) {
|
|
305
|
+
const targetPhase = stateMachine.states[transition.to];
|
|
306
|
+
const targetDescription = targetPhase
|
|
307
|
+
? targetPhase.description
|
|
308
|
+
: transition.to;
|
|
309
|
+
recommendations.immediate_actions.push(
|
|
310
|
+
`• ${transition.to}: ${targetDescription}`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Add instruction on how to transition
|
|
315
|
+
recommendations.immediate_actions.push(
|
|
316
|
+
'Use proceed_to_phase() tool when ready to transition'
|
|
317
|
+
);
|
|
318
|
+
} else {
|
|
319
|
+
recommendations.immediate_actions.push(
|
|
320
|
+
'Continue working in current phase'
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Add current phase specific guidance
|
|
325
|
+
recommendations.immediate_actions.push(
|
|
326
|
+
`Focus on: ${(phaseDefinition as YamlState).description}`
|
|
327
|
+
);
|
|
328
|
+
} else {
|
|
329
|
+
// Fallback if phase not found in state machine
|
|
330
|
+
recommendations.phase_guidance = `Current phase: ${currentPhase}`;
|
|
331
|
+
recommendations.immediate_actions.push(
|
|
332
|
+
'Continue working in current phase'
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
} catch (error) {
|
|
336
|
+
const err = error as Error;
|
|
337
|
+
this.logger.warn('Could not generate state machine recommendations', {
|
|
338
|
+
error: err.message,
|
|
339
|
+
});
|
|
340
|
+
// Basic fallback
|
|
341
|
+
recommendations.phase_guidance = `Current phase: ${conversationContext.currentPhase}`;
|
|
342
|
+
recommendations.immediate_actions.push(
|
|
343
|
+
'Continue working in current phase'
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Plan-based recommendations
|
|
348
|
+
if (planAnalysis) {
|
|
349
|
+
if (planAnalysis.active_tasks && planAnalysis.active_tasks.length > 0) {
|
|
350
|
+
recommendations.immediate_actions.push(
|
|
351
|
+
`Continue working on active tasks: ${planAnalysis.active_tasks.slice(0, 2).join(', ')}`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (
|
|
356
|
+
(!planAnalysis.active_tasks ||
|
|
357
|
+
planAnalysis.active_tasks.length === 0) &&
|
|
358
|
+
planAnalysis.completed_tasks &&
|
|
359
|
+
planAnalysis.completed_tasks.length > 0
|
|
360
|
+
) {
|
|
361
|
+
recommendations.potential_issues.push(
|
|
362
|
+
'No active tasks found - may be ready to transition to next phase'
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Always recommend calling whats_next
|
|
368
|
+
if (
|
|
369
|
+
!recommendations.immediate_actions.some(action =>
|
|
370
|
+
action.includes('whats_next')
|
|
371
|
+
)
|
|
372
|
+
) {
|
|
373
|
+
recommendations.immediate_actions.unshift(
|
|
374
|
+
'Call whats_next() to get current phase-specific guidance'
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return recommendations;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup Project Docs Handler
|
|
3
|
+
*
|
|
4
|
+
* Creates project documentation artifacts (architecture.md, requirements.md, design.md)
|
|
5
|
+
* using configurable templates OR by linking existing files via symlinks.
|
|
6
|
+
* Supports different template formats for each document type and file path linking.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { BaseToolHandler } from './base-tool-handler.js';
|
|
10
|
+
import { ProjectDocsManager } from '@codemcp/workflows-core';
|
|
11
|
+
import { TemplateOptions } from '@codemcp/workflows-core';
|
|
12
|
+
import { PathValidationUtils } from '@codemcp/workflows-core';
|
|
13
|
+
import { ServerContext } from '../types.js';
|
|
14
|
+
|
|
15
|
+
export interface SetupProjectDocsArgs {
|
|
16
|
+
architecture: string; // Template name OR file path
|
|
17
|
+
requirements: string; // Template name OR file path
|
|
18
|
+
design: string; // Template name OR file path
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SetupProjectDocsResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
created: string[];
|
|
24
|
+
linked: string[];
|
|
25
|
+
skipped: string[];
|
|
26
|
+
paths: {
|
|
27
|
+
architecture: string;
|
|
28
|
+
requirements: string;
|
|
29
|
+
design: string;
|
|
30
|
+
};
|
|
31
|
+
message: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class SetupProjectDocsHandler extends BaseToolHandler<
|
|
35
|
+
SetupProjectDocsArgs,
|
|
36
|
+
SetupProjectDocsResult
|
|
37
|
+
> {
|
|
38
|
+
private projectDocsManager: ProjectDocsManager;
|
|
39
|
+
|
|
40
|
+
constructor() {
|
|
41
|
+
super();
|
|
42
|
+
this.projectDocsManager = new ProjectDocsManager();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected async executeHandler(
|
|
46
|
+
args: SetupProjectDocsArgs,
|
|
47
|
+
context: ServerContext
|
|
48
|
+
): Promise<SetupProjectDocsResult> {
|
|
49
|
+
const projectPath = context.projectPath || process.cwd();
|
|
50
|
+
|
|
51
|
+
this.logger.info(
|
|
52
|
+
'Setting up project docs with enhanced file linking support',
|
|
53
|
+
{ args, projectPath }
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
// Get available templates for validation
|
|
58
|
+
const availableTemplates =
|
|
59
|
+
await this.projectDocsManager.templateManager.getAvailableTemplates();
|
|
60
|
+
|
|
61
|
+
// Validate and process each parameter
|
|
62
|
+
const processedArgs = await this.validateAndProcessArgs(
|
|
63
|
+
args,
|
|
64
|
+
availableTemplates,
|
|
65
|
+
projectPath
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (!processedArgs.success) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
created: [],
|
|
72
|
+
linked: [],
|
|
73
|
+
skipped: [],
|
|
74
|
+
paths: this.projectDocsManager.getDocumentPaths(projectPath),
|
|
75
|
+
message: processedArgs.error || 'Unknown error during validation',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Create/link project documents
|
|
80
|
+
if (!processedArgs.templateOptions || !processedArgs.filePaths) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'Invalid processed args: missing templateOptions or filePaths'
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result = await this.projectDocsManager.createOrLinkProjectDocs(
|
|
87
|
+
projectPath,
|
|
88
|
+
processedArgs.templateOptions,
|
|
89
|
+
processedArgs.filePaths
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Get document paths for response
|
|
93
|
+
const paths = this.projectDocsManager.getDocumentPaths(projectPath);
|
|
94
|
+
|
|
95
|
+
// Create success message
|
|
96
|
+
let message = 'Project documentation setup completed.';
|
|
97
|
+
if (result.created.length > 0) {
|
|
98
|
+
message += ` Created: ${result.created.join(', ')}.`;
|
|
99
|
+
}
|
|
100
|
+
if (result.linked.length > 0) {
|
|
101
|
+
message += ` Linked: ${result.linked.join(', ')}.`;
|
|
102
|
+
}
|
|
103
|
+
if (result.skipped.length > 0) {
|
|
104
|
+
message += ` Skipped existing: ${result.skipped.join(', ')}.`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.logger.info('Project docs setup completed', {
|
|
108
|
+
created: result.created,
|
|
109
|
+
linked: result.linked,
|
|
110
|
+
skipped: result.skipped,
|
|
111
|
+
paths,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
success: true,
|
|
116
|
+
created: result.created,
|
|
117
|
+
linked: result.linked,
|
|
118
|
+
skipped: result.skipped,
|
|
119
|
+
paths,
|
|
120
|
+
message,
|
|
121
|
+
};
|
|
122
|
+
} catch (error) {
|
|
123
|
+
this.logger.error('Failed to setup project docs', error as Error, {
|
|
124
|
+
args,
|
|
125
|
+
projectPath,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
created: [],
|
|
131
|
+
linked: [],
|
|
132
|
+
skipped: [],
|
|
133
|
+
paths: this.projectDocsManager.getDocumentPaths(projectPath),
|
|
134
|
+
message: `Failed to setup project docs: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Validate and process arguments to determine templates vs file paths
|
|
141
|
+
*/
|
|
142
|
+
private async validateAndProcessArgs(
|
|
143
|
+
args: SetupProjectDocsArgs,
|
|
144
|
+
availableTemplates: {
|
|
145
|
+
architecture: string[];
|
|
146
|
+
requirements: string[];
|
|
147
|
+
design: string[];
|
|
148
|
+
},
|
|
149
|
+
projectPath: string
|
|
150
|
+
): Promise<{
|
|
151
|
+
success: boolean;
|
|
152
|
+
error?: string;
|
|
153
|
+
templateOptions?: Partial<TemplateOptions>;
|
|
154
|
+
filePaths?: Partial<{
|
|
155
|
+
architecture: string;
|
|
156
|
+
requirements: string;
|
|
157
|
+
design: string;
|
|
158
|
+
}>;
|
|
159
|
+
}> {
|
|
160
|
+
const templateOptions: Partial<TemplateOptions> = {};
|
|
161
|
+
const filePaths: Partial<{
|
|
162
|
+
architecture: string;
|
|
163
|
+
requirements: string;
|
|
164
|
+
design: string;
|
|
165
|
+
}> = {};
|
|
166
|
+
const errors: string[] = [];
|
|
167
|
+
|
|
168
|
+
// Validate architecture parameter
|
|
169
|
+
const archValidation = await PathValidationUtils.validateParameter(
|
|
170
|
+
args.architecture,
|
|
171
|
+
availableTemplates.architecture,
|
|
172
|
+
projectPath
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (archValidation.isTemplate) {
|
|
176
|
+
templateOptions.architecture = args.architecture;
|
|
177
|
+
} else if (archValidation.isFilePath && archValidation.resolvedPath) {
|
|
178
|
+
filePaths.architecture = archValidation.resolvedPath;
|
|
179
|
+
} else {
|
|
180
|
+
errors.push(`Architecture: ${archValidation.error}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Validate requirements parameter
|
|
184
|
+
const reqValidation = await PathValidationUtils.validateParameter(
|
|
185
|
+
args.requirements,
|
|
186
|
+
availableTemplates.requirements,
|
|
187
|
+
projectPath
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (reqValidation.isTemplate) {
|
|
191
|
+
templateOptions.requirements = args.requirements;
|
|
192
|
+
} else if (reqValidation.isFilePath && reqValidation.resolvedPath) {
|
|
193
|
+
filePaths.requirements = reqValidation.resolvedPath;
|
|
194
|
+
} else {
|
|
195
|
+
errors.push(`Requirements: ${reqValidation.error}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Validate design parameter
|
|
199
|
+
const designValidation = await PathValidationUtils.validateParameter(
|
|
200
|
+
args.design,
|
|
201
|
+
availableTemplates.design,
|
|
202
|
+
projectPath
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
if (designValidation.isTemplate) {
|
|
206
|
+
templateOptions.design = args.design;
|
|
207
|
+
} else if (designValidation.isFilePath && designValidation.resolvedPath) {
|
|
208
|
+
filePaths.design = designValidation.resolvedPath;
|
|
209
|
+
} else {
|
|
210
|
+
errors.push(`Design: ${designValidation.error}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (errors.length > 0) {
|
|
214
|
+
return {
|
|
215
|
+
success: false,
|
|
216
|
+
error: `Parameter validation failed:\n${errors.join('\n')}`,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
success: true,
|
|
222
|
+
templateOptions,
|
|
223
|
+
filePaths,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|