mcp-adr-analysis-server 2.0.16 → 2.0.18
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/dist/.tsbuildinfo +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +285 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools/interactive-adr-planning-tool.d.ts +39 -0
- package/dist/src/tools/interactive-adr-planning-tool.d.ts.map +1 -0
- package/dist/src/tools/interactive-adr-planning-tool.js +1028 -0
- package/dist/src/tools/interactive-adr-planning-tool.js.map +1 -0
- package/dist/src/tools/mcp-planning-tool.d.ts +26 -0
- package/dist/src/tools/mcp-planning-tool.d.ts.map +1 -0
- package/dist/src/tools/mcp-planning-tool.js +1096 -0
- package/dist/src/tools/mcp-planning-tool.js.map +1 -0
- package/dist/src/tools/todo-management-tool-v2.d.ts.map +1 -1
- package/dist/src/tools/todo-management-tool-v2.js +69 -15
- package/dist/src/tools/todo-management-tool-v2.js.map +1 -1
- package/dist/src/utils/todo-json-manager.d.ts.map +1 -1
- package/dist/src/utils/todo-json-manager.js +5 -3
- package/dist/src/utils/todo-json-manager.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1028 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive ADR Planning Tool - Guided ADR Creation and Planning
|
|
3
|
+
*
|
|
4
|
+
* Provides an interactive, conversational approach to ADR planning that:
|
|
5
|
+
* - Guides users through structured decision-making process
|
|
6
|
+
* - Integrates research capabilities for informed decisions
|
|
7
|
+
* - Generates ADR content progressively with user feedback
|
|
8
|
+
* - Updates TODO items based on ADR decisions
|
|
9
|
+
* - Orchestrates other MCP tools for comprehensive planning
|
|
10
|
+
*
|
|
11
|
+
* Key Features:
|
|
12
|
+
* - Multi-step wizard for ADR creation
|
|
13
|
+
* - Context-aware research integration
|
|
14
|
+
* - Decision impact analysis
|
|
15
|
+
* - Automatic TODO generation from decisions
|
|
16
|
+
* - ADR template customization
|
|
17
|
+
* - Decision rationale capture
|
|
18
|
+
*
|
|
19
|
+
* Workflow Phases:
|
|
20
|
+
* 1. Problem Definition - Clarify the architectural challenge
|
|
21
|
+
* 2. Research & Analysis - Gather relevant information
|
|
22
|
+
* 3. Option Exploration - Identify and evaluate alternatives
|
|
23
|
+
* 4. Decision Making - Select approach with rationale
|
|
24
|
+
* 5. Impact Assessment - Analyze consequences and risks
|
|
25
|
+
* 6. Implementation Planning - Generate TODOs and timeline
|
|
26
|
+
* 7. ADR Generation - Create structured ADR document
|
|
27
|
+
*
|
|
28
|
+
* Integration Points:
|
|
29
|
+
* - Research tools for context gathering
|
|
30
|
+
* - Analysis tools for impact assessment
|
|
31
|
+
* - TODO management for task generation
|
|
32
|
+
* - ADR generation for document creation
|
|
33
|
+
* - Project planning for timeline integration
|
|
34
|
+
*/
|
|
35
|
+
import { z } from 'zod';
|
|
36
|
+
import { promises as fs } from 'fs';
|
|
37
|
+
import { join } from 'path';
|
|
38
|
+
import { McpAdrError } from '../types/index.js';
|
|
39
|
+
// Planning session schema
|
|
40
|
+
const PlanningSessionSchema = z.object({
|
|
41
|
+
sessionId: z.string(),
|
|
42
|
+
phase: z.enum([
|
|
43
|
+
'problem_definition',
|
|
44
|
+
'research_analysis',
|
|
45
|
+
'option_exploration',
|
|
46
|
+
'decision_making',
|
|
47
|
+
'impact_assessment',
|
|
48
|
+
'implementation_planning',
|
|
49
|
+
'adr_generation',
|
|
50
|
+
'completed'
|
|
51
|
+
]),
|
|
52
|
+
context: z.object({
|
|
53
|
+
projectPath: z.string(),
|
|
54
|
+
adrDirectory: z.string().default('docs/adrs'),
|
|
55
|
+
problemStatement: z.string().optional(),
|
|
56
|
+
researchFindings: z.array(z.object({
|
|
57
|
+
source: z.string(),
|
|
58
|
+
insight: z.string(),
|
|
59
|
+
relevance: z.string()
|
|
60
|
+
})).default([]),
|
|
61
|
+
options: z.array(z.object({
|
|
62
|
+
name: z.string(),
|
|
63
|
+
description: z.string(),
|
|
64
|
+
pros: z.array(z.string()),
|
|
65
|
+
cons: z.array(z.string()),
|
|
66
|
+
risks: z.array(z.string()),
|
|
67
|
+
effort: z.enum(['low', 'medium', 'high']),
|
|
68
|
+
confidence: z.number().min(0).max(100)
|
|
69
|
+
})).default([]),
|
|
70
|
+
selectedOption: z.object({
|
|
71
|
+
name: z.string(),
|
|
72
|
+
rationale: z.string(),
|
|
73
|
+
tradeoffs: z.array(z.string())
|
|
74
|
+
}).optional(),
|
|
75
|
+
impacts: z.object({
|
|
76
|
+
technical: z.array(z.string()).default([]),
|
|
77
|
+
business: z.array(z.string()).default([]),
|
|
78
|
+
team: z.array(z.string()).default([]),
|
|
79
|
+
risks: z.array(z.string()).default([]),
|
|
80
|
+
dependencies: z.array(z.string()).default([])
|
|
81
|
+
}).optional(),
|
|
82
|
+
implementation: z.object({
|
|
83
|
+
phases: z.array(z.string()).default([]),
|
|
84
|
+
tasks: z.array(z.object({
|
|
85
|
+
description: z.string(),
|
|
86
|
+
priority: z.enum(['low', 'medium', 'high', 'critical']),
|
|
87
|
+
effort: z.string(),
|
|
88
|
+
assignee: z.string().optional()
|
|
89
|
+
})).default([]),
|
|
90
|
+
timeline: z.string().optional(),
|
|
91
|
+
successCriteria: z.array(z.string()).default([])
|
|
92
|
+
}).optional(),
|
|
93
|
+
adrContent: z.string().optional()
|
|
94
|
+
}),
|
|
95
|
+
metadata: z.object({
|
|
96
|
+
createdAt: z.string(),
|
|
97
|
+
updatedAt: z.string(),
|
|
98
|
+
lastAction: z.string(),
|
|
99
|
+
nextSteps: z.array(z.string()).default([])
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
// Input schema for tool operations
|
|
103
|
+
const InteractiveAdrPlanningSchema = z.object({
|
|
104
|
+
operation: z.enum([
|
|
105
|
+
'start_session',
|
|
106
|
+
'continue_session',
|
|
107
|
+
'provide_input',
|
|
108
|
+
'request_research',
|
|
109
|
+
'evaluate_options',
|
|
110
|
+
'make_decision',
|
|
111
|
+
'assess_impact',
|
|
112
|
+
'plan_implementation',
|
|
113
|
+
'generate_adr',
|
|
114
|
+
'update_todos',
|
|
115
|
+
'get_guidance',
|
|
116
|
+
'save_session',
|
|
117
|
+
'complete_session'
|
|
118
|
+
]),
|
|
119
|
+
sessionId: z.string().optional(),
|
|
120
|
+
input: z.any().optional(),
|
|
121
|
+
projectPath: z.string(),
|
|
122
|
+
autoResearch: z.boolean().default(true).describe('Automatically trigger research when needed'),
|
|
123
|
+
generateTodos: z.boolean().default(true).describe('Automatically generate TODO items from decisions')
|
|
124
|
+
});
|
|
125
|
+
// Session storage
|
|
126
|
+
const sessions = new Map();
|
|
127
|
+
/**
|
|
128
|
+
* Get or create cache directory
|
|
129
|
+
*/
|
|
130
|
+
async function getCacheDir(projectPath) {
|
|
131
|
+
const cacheDir = join(projectPath, '.mcp-adr-cache');
|
|
132
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
133
|
+
return cacheDir;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Save session to cache
|
|
137
|
+
*/
|
|
138
|
+
async function saveSession(session) {
|
|
139
|
+
const cacheDir = await getCacheDir(session.context.projectPath);
|
|
140
|
+
const sessionFile = join(cacheDir, `adr-planning-session-${session.sessionId}.json`);
|
|
141
|
+
await fs.writeFile(sessionFile, JSON.stringify(session, null, 2));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Load session from cache
|
|
145
|
+
*/
|
|
146
|
+
async function loadSession(projectPath, sessionId) {
|
|
147
|
+
try {
|
|
148
|
+
const cacheDir = await getCacheDir(projectPath);
|
|
149
|
+
const sessionFile = join(cacheDir, `adr-planning-session-${sessionId}.json`);
|
|
150
|
+
const data = await fs.readFile(sessionFile, 'utf-8');
|
|
151
|
+
return PlanningSessionSchema.parse(JSON.parse(data));
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Start a new planning session
|
|
159
|
+
*/
|
|
160
|
+
async function startSession(args) {
|
|
161
|
+
const sessionId = `adr-${Date.now()}`;
|
|
162
|
+
const session = {
|
|
163
|
+
sessionId,
|
|
164
|
+
phase: 'problem_definition',
|
|
165
|
+
context: {
|
|
166
|
+
projectPath: args.projectPath,
|
|
167
|
+
adrDirectory: 'docs/adrs',
|
|
168
|
+
researchFindings: [],
|
|
169
|
+
options: []
|
|
170
|
+
},
|
|
171
|
+
metadata: {
|
|
172
|
+
createdAt: new Date().toISOString(),
|
|
173
|
+
updatedAt: new Date().toISOString(),
|
|
174
|
+
lastAction: 'Session started',
|
|
175
|
+
nextSteps: [
|
|
176
|
+
'Define the architectural problem or decision point',
|
|
177
|
+
'Identify key stakeholders and constraints',
|
|
178
|
+
'Determine success criteria'
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
sessions.set(sessionId, session);
|
|
183
|
+
await saveSession(session);
|
|
184
|
+
return {
|
|
185
|
+
success: true,
|
|
186
|
+
sessionId,
|
|
187
|
+
phase: session.phase,
|
|
188
|
+
guidance: `# ADR Planning Session Started
|
|
189
|
+
|
|
190
|
+
Welcome to the interactive ADR planning process. Let's start by defining the problem.
|
|
191
|
+
|
|
192
|
+
## Current Phase: Problem Definition
|
|
193
|
+
|
|
194
|
+
Please provide:
|
|
195
|
+
1. **What architectural decision needs to be made?**
|
|
196
|
+
2. **Why is this decision necessary now?**
|
|
197
|
+
3. **What are the key constraints?**
|
|
198
|
+
4. **Who are the stakeholders?**
|
|
199
|
+
5. **What would success look like?**
|
|
200
|
+
|
|
201
|
+
### Next Steps:
|
|
202
|
+
${session.metadata.nextSteps.map(step => `- ${step}`).join('\n')}
|
|
203
|
+
|
|
204
|
+
Use \`provide_input\` with your problem statement to continue.`,
|
|
205
|
+
nextAction: 'provide_input'
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Continue an existing session
|
|
210
|
+
*/
|
|
211
|
+
async function continueSession(args) {
|
|
212
|
+
if (!args.sessionId) {
|
|
213
|
+
throw new McpAdrError('Session ID required to continue session', 'INVALID_INPUT');
|
|
214
|
+
}
|
|
215
|
+
let session = sessions.get(args.sessionId);
|
|
216
|
+
if (!session) {
|
|
217
|
+
const loadedSession = await loadSession(args.projectPath, args.sessionId);
|
|
218
|
+
if (!loadedSession) {
|
|
219
|
+
throw new McpAdrError(`Session ${args.sessionId} not found`, 'SESSION_NOT_FOUND');
|
|
220
|
+
}
|
|
221
|
+
session = loadedSession;
|
|
222
|
+
sessions.set(args.sessionId, session);
|
|
223
|
+
}
|
|
224
|
+
const phaseGuidance = {
|
|
225
|
+
problem_definition: 'Define the architectural problem and constraints',
|
|
226
|
+
research_analysis: 'Research existing solutions and gather context',
|
|
227
|
+
option_exploration: 'Identify and evaluate alternative approaches',
|
|
228
|
+
decision_making: 'Select the best option with clear rationale',
|
|
229
|
+
impact_assessment: 'Analyze the impacts and risks of the decision',
|
|
230
|
+
implementation_planning: 'Plan the implementation with tasks and timeline',
|
|
231
|
+
adr_generation: 'Generate the final ADR document',
|
|
232
|
+
completed: 'Session completed - ADR has been generated'
|
|
233
|
+
};
|
|
234
|
+
return {
|
|
235
|
+
success: true,
|
|
236
|
+
sessionId: args.sessionId,
|
|
237
|
+
phase: session.phase,
|
|
238
|
+
context: session.context,
|
|
239
|
+
guidance: phaseGuidance[session.phase],
|
|
240
|
+
nextSteps: session.metadata.nextSteps,
|
|
241
|
+
progress: getSessionProgress(session)
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Process user input based on current phase
|
|
246
|
+
*/
|
|
247
|
+
async function provideInput(args) {
|
|
248
|
+
if (!args.sessionId) {
|
|
249
|
+
throw new McpAdrError('Session ID required', 'INVALID_INPUT');
|
|
250
|
+
}
|
|
251
|
+
const session = sessions.get(args.sessionId);
|
|
252
|
+
if (!session) {
|
|
253
|
+
throw new McpAdrError(`Session ${args.sessionId} not found`, 'SESSION_NOT_FOUND');
|
|
254
|
+
}
|
|
255
|
+
let response = { success: true, sessionId: args.sessionId };
|
|
256
|
+
switch (session.phase) {
|
|
257
|
+
case 'problem_definition':
|
|
258
|
+
response = await handleProblemDefinition(session, args.input);
|
|
259
|
+
break;
|
|
260
|
+
case 'research_analysis':
|
|
261
|
+
response = await handleResearchAnalysis(session, args.input);
|
|
262
|
+
break;
|
|
263
|
+
case 'option_exploration':
|
|
264
|
+
response = await handleOptionExploration(session, args.input);
|
|
265
|
+
break;
|
|
266
|
+
case 'decision_making':
|
|
267
|
+
response = await handleDecisionMaking(session, args.input);
|
|
268
|
+
break;
|
|
269
|
+
case 'impact_assessment':
|
|
270
|
+
response = await handleImpactAssessment(session, args.input);
|
|
271
|
+
break;
|
|
272
|
+
case 'implementation_planning':
|
|
273
|
+
response = await handleImplementationPlanning(session, args.input);
|
|
274
|
+
break;
|
|
275
|
+
case 'adr_generation':
|
|
276
|
+
response = await handleAdrGeneration(session, args.input);
|
|
277
|
+
break;
|
|
278
|
+
default:
|
|
279
|
+
throw new McpAdrError(`Invalid phase: ${session.phase}`, 'INVALID_PHASE');
|
|
280
|
+
}
|
|
281
|
+
session.metadata.updatedAt = new Date().toISOString();
|
|
282
|
+
await saveSession(session);
|
|
283
|
+
return response;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Handle problem definition phase
|
|
287
|
+
*/
|
|
288
|
+
async function handleProblemDefinition(session, input) {
|
|
289
|
+
session.context.problemStatement = input.problemStatement || input;
|
|
290
|
+
session.phase = 'research_analysis';
|
|
291
|
+
session.metadata.lastAction = 'Problem defined';
|
|
292
|
+
session.metadata.nextSteps = [
|
|
293
|
+
'Research existing solutions',
|
|
294
|
+
'Analyze current architecture',
|
|
295
|
+
'Identify relevant patterns and practices'
|
|
296
|
+
];
|
|
297
|
+
return {
|
|
298
|
+
success: true,
|
|
299
|
+
sessionId: session.sessionId,
|
|
300
|
+
phase: session.phase,
|
|
301
|
+
guidance: `# Problem Defined
|
|
302
|
+
|
|
303
|
+
**Problem Statement:** ${session.context.problemStatement}
|
|
304
|
+
|
|
305
|
+
## Next Phase: Research & Analysis
|
|
306
|
+
|
|
307
|
+
I'll help you research this topic. We can:
|
|
308
|
+
1. Search for existing solutions and patterns
|
|
309
|
+
2. Analyze your current codebase for context
|
|
310
|
+
3. Research industry best practices
|
|
311
|
+
4. Identify relevant architectural patterns
|
|
312
|
+
|
|
313
|
+
Would you like me to:
|
|
314
|
+
- Automatically research this topic? (use \`request_research\`)
|
|
315
|
+
- Or provide your own research findings? (use \`provide_input\` with findings)`,
|
|
316
|
+
nextAction: 'request_research',
|
|
317
|
+
autoResearch: true
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Handle research analysis phase
|
|
322
|
+
*/
|
|
323
|
+
async function handleResearchAnalysis(session, input) {
|
|
324
|
+
if (input.researchFindings) {
|
|
325
|
+
session.context.researchFindings = input.researchFindings;
|
|
326
|
+
}
|
|
327
|
+
session.phase = 'option_exploration';
|
|
328
|
+
session.metadata.lastAction = 'Research completed';
|
|
329
|
+
session.metadata.nextSteps = [
|
|
330
|
+
'Identify possible solutions',
|
|
331
|
+
'Evaluate pros and cons',
|
|
332
|
+
'Assess implementation effort'
|
|
333
|
+
];
|
|
334
|
+
return {
|
|
335
|
+
success: true,
|
|
336
|
+
sessionId: session.sessionId,
|
|
337
|
+
phase: session.phase,
|
|
338
|
+
researchSummary: session.context.researchFindings,
|
|
339
|
+
guidance: `# Research Complete
|
|
340
|
+
|
|
341
|
+
## Key Findings:
|
|
342
|
+
${session.context.researchFindings.map(f => `- **${f.source}**: ${f.insight}`).join('\n')}
|
|
343
|
+
|
|
344
|
+
## Next Phase: Option Exploration
|
|
345
|
+
|
|
346
|
+
Based on the research, let's identify and evaluate different approaches.
|
|
347
|
+
|
|
348
|
+
Please provide options in this format:
|
|
349
|
+
\`\`\`json
|
|
350
|
+
{
|
|
351
|
+
"options": [
|
|
352
|
+
{
|
|
353
|
+
"name": "Option Name",
|
|
354
|
+
"description": "What this approach involves",
|
|
355
|
+
"pros": ["Advantage 1", "Advantage 2"],
|
|
356
|
+
"cons": ["Disadvantage 1", "Disadvantage 2"],
|
|
357
|
+
"risks": ["Risk 1", "Risk 2"],
|
|
358
|
+
"effort": "medium",
|
|
359
|
+
"confidence": 75
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
}
|
|
363
|
+
\`\`\`
|
|
364
|
+
|
|
365
|
+
Or use \`evaluate_options\` to have me help generate options based on the research.`,
|
|
366
|
+
nextAction: 'evaluate_options'
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Handle option exploration phase
|
|
371
|
+
*/
|
|
372
|
+
async function handleOptionExploration(session, input) {
|
|
373
|
+
if (input.options) {
|
|
374
|
+
session.context.options = input.options;
|
|
375
|
+
}
|
|
376
|
+
session.phase = 'decision_making';
|
|
377
|
+
session.metadata.lastAction = 'Options evaluated';
|
|
378
|
+
session.metadata.nextSteps = [
|
|
379
|
+
'Compare options against criteria',
|
|
380
|
+
'Select best approach',
|
|
381
|
+
'Document rationale'
|
|
382
|
+
];
|
|
383
|
+
const optionComparison = session.context.options.map(opt => `### ${opt.name} (Confidence: ${opt.confidence}%)
|
|
384
|
+
**Description:** ${opt.description}
|
|
385
|
+
**Pros:** ${opt.pros.join(', ')}
|
|
386
|
+
**Cons:** ${opt.cons.join(', ')}
|
|
387
|
+
**Effort:** ${opt.effort}
|
|
388
|
+
**Risks:** ${opt.risks.join(', ')}`).join('\n\n');
|
|
389
|
+
return {
|
|
390
|
+
success: true,
|
|
391
|
+
sessionId: session.sessionId,
|
|
392
|
+
phase: session.phase,
|
|
393
|
+
options: session.context.options,
|
|
394
|
+
guidance: `# Options Evaluated
|
|
395
|
+
|
|
396
|
+
## Available Options:
|
|
397
|
+
${optionComparison}
|
|
398
|
+
|
|
399
|
+
## Next Phase: Decision Making
|
|
400
|
+
|
|
401
|
+
Review the options and select the best approach.
|
|
402
|
+
|
|
403
|
+
Provide your decision with:
|
|
404
|
+
\`\`\`json
|
|
405
|
+
{
|
|
406
|
+
"selectedOption": {
|
|
407
|
+
"name": "Chosen Option Name",
|
|
408
|
+
"rationale": "Why this option was selected",
|
|
409
|
+
"tradeoffs": ["What we're giving up", "What we're gaining"]
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
\`\`\`
|
|
413
|
+
|
|
414
|
+
Use \`make_decision\` to proceed.`,
|
|
415
|
+
nextAction: 'make_decision'
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Handle decision making phase
|
|
420
|
+
*/
|
|
421
|
+
async function handleDecisionMaking(session, input) {
|
|
422
|
+
if (input.selectedOption) {
|
|
423
|
+
session.context.selectedOption = input.selectedOption;
|
|
424
|
+
}
|
|
425
|
+
session.phase = 'impact_assessment';
|
|
426
|
+
session.metadata.lastAction = 'Decision made';
|
|
427
|
+
session.metadata.nextSteps = [
|
|
428
|
+
'Assess technical impacts',
|
|
429
|
+
'Evaluate business impacts',
|
|
430
|
+
'Identify risks and dependencies'
|
|
431
|
+
];
|
|
432
|
+
return {
|
|
433
|
+
success: true,
|
|
434
|
+
sessionId: session.sessionId,
|
|
435
|
+
phase: session.phase,
|
|
436
|
+
decision: session.context.selectedOption,
|
|
437
|
+
guidance: `# Decision Made
|
|
438
|
+
|
|
439
|
+
**Selected:** ${session.context.selectedOption?.name}
|
|
440
|
+
**Rationale:** ${session.context.selectedOption?.rationale}
|
|
441
|
+
|
|
442
|
+
## Next Phase: Impact Assessment
|
|
443
|
+
|
|
444
|
+
Let's analyze the impacts of this decision.
|
|
445
|
+
|
|
446
|
+
Provide impact analysis:
|
|
447
|
+
\`\`\`json
|
|
448
|
+
{
|
|
449
|
+
"impacts": {
|
|
450
|
+
"technical": ["Impact on architecture", "Performance considerations"],
|
|
451
|
+
"business": ["Time to market", "Cost implications"],
|
|
452
|
+
"team": ["Training needs", "Skill requirements"],
|
|
453
|
+
"risks": ["Implementation risks", "Migration challenges"],
|
|
454
|
+
"dependencies": ["Required tools", "External services"]
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
\`\`\`
|
|
458
|
+
|
|
459
|
+
Use \`assess_impact\` to continue.`,
|
|
460
|
+
nextAction: 'assess_impact'
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Handle impact assessment phase
|
|
465
|
+
*/
|
|
466
|
+
async function handleImpactAssessment(session, input) {
|
|
467
|
+
if (input.impacts) {
|
|
468
|
+
session.context.impacts = input.impacts;
|
|
469
|
+
}
|
|
470
|
+
session.phase = 'implementation_planning';
|
|
471
|
+
session.metadata.lastAction = 'Impacts assessed';
|
|
472
|
+
session.metadata.nextSteps = [
|
|
473
|
+
'Define implementation phases',
|
|
474
|
+
'Create task list',
|
|
475
|
+
'Set timeline and milestones'
|
|
476
|
+
];
|
|
477
|
+
return {
|
|
478
|
+
success: true,
|
|
479
|
+
sessionId: session.sessionId,
|
|
480
|
+
phase: session.phase,
|
|
481
|
+
impacts: session.context.impacts,
|
|
482
|
+
guidance: `# Impact Assessment Complete
|
|
483
|
+
|
|
484
|
+
## Identified Impacts:
|
|
485
|
+
- **Technical:** ${session.context.impacts?.technical.length} impacts
|
|
486
|
+
- **Business:** ${session.context.impacts?.business.length} impacts
|
|
487
|
+
- **Team:** ${session.context.impacts?.team.length} impacts
|
|
488
|
+
- **Risks:** ${session.context.impacts?.risks.length} risks
|
|
489
|
+
- **Dependencies:** ${session.context.impacts?.dependencies.length} dependencies
|
|
490
|
+
|
|
491
|
+
## Next Phase: Implementation Planning
|
|
492
|
+
|
|
493
|
+
Let's create an implementation plan with tasks and timeline.
|
|
494
|
+
|
|
495
|
+
Provide implementation details:
|
|
496
|
+
\`\`\`json
|
|
497
|
+
{
|
|
498
|
+
"implementation": {
|
|
499
|
+
"phases": ["Phase 1: Setup", "Phase 2: Core Implementation"],
|
|
500
|
+
"tasks": [
|
|
501
|
+
{
|
|
502
|
+
"description": "Task description",
|
|
503
|
+
"priority": "high",
|
|
504
|
+
"effort": "2 days",
|
|
505
|
+
"assignee": "team-member"
|
|
506
|
+
}
|
|
507
|
+
],
|
|
508
|
+
"timeline": "6 weeks",
|
|
509
|
+
"successCriteria": ["Criteria 1", "Criteria 2"]
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
\`\`\`
|
|
513
|
+
|
|
514
|
+
Use \`plan_implementation\` to continue.`,
|
|
515
|
+
nextAction: 'plan_implementation'
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Handle implementation planning phase
|
|
520
|
+
*/
|
|
521
|
+
async function handleImplementationPlanning(session, input) {
|
|
522
|
+
if (input.implementation) {
|
|
523
|
+
session.context.implementation = input.implementation;
|
|
524
|
+
}
|
|
525
|
+
session.phase = 'adr_generation';
|
|
526
|
+
session.metadata.lastAction = 'Implementation planned';
|
|
527
|
+
session.metadata.nextSteps = [
|
|
528
|
+
'Generate ADR document',
|
|
529
|
+
'Create TODO items',
|
|
530
|
+
'Update project plan'
|
|
531
|
+
];
|
|
532
|
+
return {
|
|
533
|
+
success: true,
|
|
534
|
+
sessionId: session.sessionId,
|
|
535
|
+
phase: session.phase,
|
|
536
|
+
implementation: session.context.implementation,
|
|
537
|
+
guidance: `# Implementation Plan Complete
|
|
538
|
+
|
|
539
|
+
## Plan Summary:
|
|
540
|
+
- **Phases:** ${session.context.implementation?.phases.length} phases
|
|
541
|
+
- **Tasks:** ${session.context.implementation?.tasks.length} tasks
|
|
542
|
+
- **Timeline:** ${session.context.implementation?.timeline}
|
|
543
|
+
- **Success Criteria:** ${session.context.implementation?.successCriteria.length} criteria
|
|
544
|
+
|
|
545
|
+
## Next Phase: ADR Generation
|
|
546
|
+
|
|
547
|
+
Ready to generate the ADR document with all the information gathered.
|
|
548
|
+
|
|
549
|
+
The ADR will include:
|
|
550
|
+
- Problem statement and context
|
|
551
|
+
- Considered options with evaluation
|
|
552
|
+
- Decision and rationale
|
|
553
|
+
- Impacts and consequences
|
|
554
|
+
- Implementation plan
|
|
555
|
+
|
|
556
|
+
Use \`generate_adr\` to create the ADR document.`,
|
|
557
|
+
nextAction: 'generate_adr',
|
|
558
|
+
readyToGenerate: true
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Handle ADR generation phase
|
|
563
|
+
*/
|
|
564
|
+
async function handleAdrGeneration(session, _input) {
|
|
565
|
+
const adrContent = generateAdrContent(session);
|
|
566
|
+
session.context.adrContent = adrContent;
|
|
567
|
+
// Save ADR to file
|
|
568
|
+
const adrDir = join(session.context.projectPath, session.context.adrDirectory);
|
|
569
|
+
await fs.mkdir(adrDir, { recursive: true });
|
|
570
|
+
const adrNumber = await getNextAdrNumber(adrDir);
|
|
571
|
+
const adrFilename = `${String(adrNumber).padStart(4, '0')}-${session.context.selectedOption?.name.toLowerCase().replace(/\s+/g, '-')}.md`;
|
|
572
|
+
const adrPath = join(adrDir, adrFilename);
|
|
573
|
+
await fs.writeFile(adrPath, adrContent);
|
|
574
|
+
session.phase = 'completed';
|
|
575
|
+
session.metadata.lastAction = 'ADR generated';
|
|
576
|
+
session.metadata.nextSteps = [];
|
|
577
|
+
return {
|
|
578
|
+
success: true,
|
|
579
|
+
sessionId: session.sessionId,
|
|
580
|
+
phase: session.phase,
|
|
581
|
+
adrPath,
|
|
582
|
+
adrContent,
|
|
583
|
+
guidance: `# ADR Generated Successfully!
|
|
584
|
+
|
|
585
|
+
## ADR Details:
|
|
586
|
+
- **File:** ${adrPath}
|
|
587
|
+
- **Decision:** ${session.context.selectedOption?.name}
|
|
588
|
+
- **Status:** Draft
|
|
589
|
+
|
|
590
|
+
## Next Steps:
|
|
591
|
+
1. Review the generated ADR
|
|
592
|
+
2. TODO items have been created based on the implementation plan
|
|
593
|
+
3. Project plan has been updated
|
|
594
|
+
|
|
595
|
+
You can:
|
|
596
|
+
- Use \`update_todos\` to generate TODO items
|
|
597
|
+
- Use \`complete_session\` to finish and clean up
|
|
598
|
+
- Make manual edits to the ADR file as needed`,
|
|
599
|
+
nextAction: 'update_todos',
|
|
600
|
+
completed: true
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Request research on the problem
|
|
605
|
+
*/
|
|
606
|
+
async function requestResearch(args) {
|
|
607
|
+
if (!args.sessionId) {
|
|
608
|
+
throw new McpAdrError('Session ID required', 'INVALID_INPUT');
|
|
609
|
+
}
|
|
610
|
+
const session = sessions.get(args.sessionId);
|
|
611
|
+
if (!session) {
|
|
612
|
+
throw new McpAdrError(`Session ${args.sessionId} not found`, 'SESSION_NOT_FOUND');
|
|
613
|
+
}
|
|
614
|
+
// Simulate research integration (would integrate with actual research tools)
|
|
615
|
+
const researchFindings = [
|
|
616
|
+
{
|
|
617
|
+
source: 'Industry Best Practices',
|
|
618
|
+
insight: `For ${session.context.problemStatement}, microservices architecture is commonly used for scalability`,
|
|
619
|
+
relevance: 'High - Addresses core scalability concerns'
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
source: 'Current Codebase Analysis',
|
|
623
|
+
insight: 'Existing monolithic structure with clear domain boundaries',
|
|
624
|
+
relevance: 'High - Shows feasibility of decomposition'
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
source: 'Team Experience',
|
|
628
|
+
insight: 'Team has limited microservices experience but strong domain knowledge',
|
|
629
|
+
relevance: 'Medium - Impacts implementation approach'
|
|
630
|
+
}
|
|
631
|
+
];
|
|
632
|
+
session.context.researchFindings = researchFindings;
|
|
633
|
+
session.phase = 'option_exploration';
|
|
634
|
+
session.metadata.updatedAt = new Date().toISOString();
|
|
635
|
+
await saveSession(session);
|
|
636
|
+
return {
|
|
637
|
+
success: true,
|
|
638
|
+
sessionId: args.sessionId,
|
|
639
|
+
researchFindings,
|
|
640
|
+
guidance: 'Research complete. Moving to option exploration phase.',
|
|
641
|
+
nextAction: 'evaluate_options'
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Evaluate options based on research
|
|
646
|
+
*/
|
|
647
|
+
async function evaluateOptions(args) {
|
|
648
|
+
if (!args.sessionId) {
|
|
649
|
+
throw new McpAdrError('Session ID required', 'INVALID_INPUT');
|
|
650
|
+
}
|
|
651
|
+
const session = sessions.get(args.sessionId);
|
|
652
|
+
if (!session) {
|
|
653
|
+
throw new McpAdrError(`Session ${args.sessionId} not found`, 'SESSION_NOT_FOUND');
|
|
654
|
+
}
|
|
655
|
+
// Generate options based on problem and research
|
|
656
|
+
const options = [
|
|
657
|
+
{
|
|
658
|
+
name: 'Full Microservices Migration',
|
|
659
|
+
description: 'Complete decomposition into microservices',
|
|
660
|
+
pros: ['Maximum scalability', 'Independent deployment', 'Technology flexibility'],
|
|
661
|
+
cons: ['High complexity', 'Significant refactoring', 'Operational overhead'],
|
|
662
|
+
risks: ['Team learning curve', 'Integration complexity', 'Data consistency'],
|
|
663
|
+
effort: 'high',
|
|
664
|
+
confidence: 60
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
name: 'Modular Monolith',
|
|
668
|
+
description: 'Keep monolithic deployment but enforce module boundaries',
|
|
669
|
+
pros: ['Simpler operations', 'Easier data consistency', 'Gradual evolution path'],
|
|
670
|
+
cons: ['Limited scalability', 'Shared deployment', 'Technology constraints'],
|
|
671
|
+
risks: ['Module boundary violations', 'Scaling limitations'],
|
|
672
|
+
effort: 'medium',
|
|
673
|
+
confidence: 80
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
name: 'Selective Service Extraction',
|
|
677
|
+
description: 'Extract only high-traffic components as services',
|
|
678
|
+
pros: ['Targeted scalability', 'Lower complexity', 'Pragmatic approach'],
|
|
679
|
+
cons: ['Partial benefits', 'Mixed architecture'],
|
|
680
|
+
risks: ['Service boundary decisions', 'Integration points'],
|
|
681
|
+
effort: 'medium',
|
|
682
|
+
confidence: 85
|
|
683
|
+
}
|
|
684
|
+
];
|
|
685
|
+
session.context.options = options;
|
|
686
|
+
session.phase = 'decision_making';
|
|
687
|
+
session.metadata.updatedAt = new Date().toISOString();
|
|
688
|
+
await saveSession(session);
|
|
689
|
+
return {
|
|
690
|
+
success: true,
|
|
691
|
+
sessionId: args.sessionId,
|
|
692
|
+
options,
|
|
693
|
+
guidance: 'Options evaluated. Ready for decision making.',
|
|
694
|
+
nextAction: 'make_decision'
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Update TODO items based on ADR decisions
|
|
699
|
+
*/
|
|
700
|
+
async function updateTodos(args) {
|
|
701
|
+
if (!args.sessionId) {
|
|
702
|
+
throw new McpAdrError('Session ID required', 'INVALID_INPUT');
|
|
703
|
+
}
|
|
704
|
+
const session = sessions.get(args.sessionId);
|
|
705
|
+
if (!session) {
|
|
706
|
+
throw new McpAdrError(`Session ${args.sessionId} not found`, 'SESSION_NOT_FOUND');
|
|
707
|
+
}
|
|
708
|
+
if (!session.context.implementation) {
|
|
709
|
+
throw new McpAdrError('No implementation plan available', 'MISSING_IMPLEMENTATION');
|
|
710
|
+
}
|
|
711
|
+
// Generate TODO items from implementation tasks
|
|
712
|
+
const todos = session.context.implementation.tasks.map((task, index) => ({
|
|
713
|
+
id: `adr-${session.sessionId}-task-${index + 1}`,
|
|
714
|
+
description: task.description,
|
|
715
|
+
priority: task.priority,
|
|
716
|
+
effort: task.effort,
|
|
717
|
+
assignee: task.assignee,
|
|
718
|
+
adrLink: session.context.adrContent ? 'Generated ADR' : undefined,
|
|
719
|
+
status: 'pending'
|
|
720
|
+
}));
|
|
721
|
+
return {
|
|
722
|
+
success: true,
|
|
723
|
+
sessionId: args.sessionId,
|
|
724
|
+
todosGenerated: todos.length,
|
|
725
|
+
todos,
|
|
726
|
+
guidance: `Generated ${todos.length} TODO items from the implementation plan.`
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Get guidance for current phase
|
|
731
|
+
*/
|
|
732
|
+
async function getGuidance(args) {
|
|
733
|
+
if (!args.sessionId) {
|
|
734
|
+
throw new McpAdrError('Session ID required', 'INVALID_INPUT');
|
|
735
|
+
}
|
|
736
|
+
const session = sessions.get(args.sessionId);
|
|
737
|
+
if (!session) {
|
|
738
|
+
throw new McpAdrError(`Session ${args.sessionId} not found`, 'SESSION_NOT_FOUND');
|
|
739
|
+
}
|
|
740
|
+
const phaseGuidance = {
|
|
741
|
+
problem_definition: {
|
|
742
|
+
tips: [
|
|
743
|
+
'Be specific about the problem',
|
|
744
|
+
'Include quantifiable metrics if possible',
|
|
745
|
+
'Identify all stakeholders',
|
|
746
|
+
'Define clear success criteria'
|
|
747
|
+
],
|
|
748
|
+
questions: [
|
|
749
|
+
'What specific problem are we solving?',
|
|
750
|
+
'Why does this need to be addressed now?',
|
|
751
|
+
'What happens if we do nothing?',
|
|
752
|
+
'How will we measure success?'
|
|
753
|
+
],
|
|
754
|
+
examples: [
|
|
755
|
+
'Application response time exceeds 3 seconds under load',
|
|
756
|
+
'Cannot deploy services independently',
|
|
757
|
+
'Database becoming a bottleneck for scaling'
|
|
758
|
+
]
|
|
759
|
+
},
|
|
760
|
+
research_analysis: {
|
|
761
|
+
tips: [
|
|
762
|
+
'Look for industry patterns',
|
|
763
|
+
'Analyze similar problems in your codebase',
|
|
764
|
+
'Consider team capabilities',
|
|
765
|
+
'Research tooling and frameworks'
|
|
766
|
+
],
|
|
767
|
+
questions: [
|
|
768
|
+
'What solutions have others used?',
|
|
769
|
+
'What patterns apply to our context?',
|
|
770
|
+
'What are the technical constraints?',
|
|
771
|
+
'What skills does the team have?'
|
|
772
|
+
]
|
|
773
|
+
},
|
|
774
|
+
option_exploration: {
|
|
775
|
+
tips: [
|
|
776
|
+
'Consider at least 3 options',
|
|
777
|
+
'Include a "do nothing" option if relevant',
|
|
778
|
+
'Be honest about pros and cons',
|
|
779
|
+
'Estimate effort realistically'
|
|
780
|
+
],
|
|
781
|
+
questions: [
|
|
782
|
+
'What are all possible approaches?',
|
|
783
|
+
'What are the tradeoffs?',
|
|
784
|
+
'How much effort is each option?',
|
|
785
|
+
'What are the risks?'
|
|
786
|
+
]
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
const currentGuidance = phaseGuidance[session.phase] || {
|
|
790
|
+
tips: ['Follow the structured process'],
|
|
791
|
+
questions: ['What do you need help with?']
|
|
792
|
+
};
|
|
793
|
+
return {
|
|
794
|
+
success: true,
|
|
795
|
+
sessionId: args.sessionId,
|
|
796
|
+
phase: session.phase,
|
|
797
|
+
guidance: currentGuidance,
|
|
798
|
+
progress: getSessionProgress(session),
|
|
799
|
+
nextSteps: session.metadata.nextSteps
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Complete the planning session
|
|
804
|
+
*/
|
|
805
|
+
async function completeSession(args) {
|
|
806
|
+
if (!args.sessionId) {
|
|
807
|
+
throw new McpAdrError('Session ID required', 'INVALID_INPUT');
|
|
808
|
+
}
|
|
809
|
+
const session = sessions.get(args.sessionId);
|
|
810
|
+
if (!session) {
|
|
811
|
+
throw new McpAdrError(`Session ${args.sessionId} not found`, 'SESSION_NOT_FOUND');
|
|
812
|
+
}
|
|
813
|
+
// Clean up session
|
|
814
|
+
sessions.delete(args.sessionId);
|
|
815
|
+
// Archive session file
|
|
816
|
+
const cacheDir = await getCacheDir(session.context.projectPath);
|
|
817
|
+
const sessionFile = join(cacheDir, `adr-planning-session-${session.sessionId}.json`);
|
|
818
|
+
const archiveFile = join(cacheDir, `archived-adr-planning-session-${session.sessionId}.json`);
|
|
819
|
+
try {
|
|
820
|
+
await fs.rename(sessionFile, archiveFile);
|
|
821
|
+
}
|
|
822
|
+
catch (error) {
|
|
823
|
+
console.warn('Failed to archive session file:', error);
|
|
824
|
+
}
|
|
825
|
+
return {
|
|
826
|
+
success: true,
|
|
827
|
+
sessionId: args.sessionId,
|
|
828
|
+
summary: {
|
|
829
|
+
problem: session.context.problemStatement,
|
|
830
|
+
decision: session.context.selectedOption?.name,
|
|
831
|
+
adrGenerated: !!session.context.adrContent,
|
|
832
|
+
tasksCreated: session.context.implementation?.tasks.length || 0,
|
|
833
|
+
duration: calculateDuration(session.metadata.createdAt, session.metadata.updatedAt)
|
|
834
|
+
},
|
|
835
|
+
message: 'Planning session completed successfully!'
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Helper function to generate ADR content
|
|
840
|
+
*/
|
|
841
|
+
function generateAdrContent(session) {
|
|
842
|
+
const date = new Date().toISOString().split('T')[0];
|
|
843
|
+
const status = 'Draft';
|
|
844
|
+
return `# ${session.context.selectedOption?.name}
|
|
845
|
+
|
|
846
|
+
**Status:** ${status}
|
|
847
|
+
**Date:** ${date}
|
|
848
|
+
**Decision Made By:** [Team/Architect Name]
|
|
849
|
+
|
|
850
|
+
## Context
|
|
851
|
+
|
|
852
|
+
${session.context.problemStatement}
|
|
853
|
+
|
|
854
|
+
### Research Findings
|
|
855
|
+
|
|
856
|
+
${session.context.researchFindings.map(f => `- **${f.source}**: ${f.insight}`).join('\n')}
|
|
857
|
+
|
|
858
|
+
## Decision
|
|
859
|
+
|
|
860
|
+
We have decided to implement **${session.context.selectedOption?.name}**.
|
|
861
|
+
|
|
862
|
+
### Rationale
|
|
863
|
+
|
|
864
|
+
${session.context.selectedOption?.rationale}
|
|
865
|
+
|
|
866
|
+
### Trade-offs
|
|
867
|
+
|
|
868
|
+
${session.context.selectedOption?.tradeoffs.map(t => `- ${t}`).join('\n')}
|
|
869
|
+
|
|
870
|
+
## Considered Options
|
|
871
|
+
|
|
872
|
+
${session.context.options.map(opt => `
|
|
873
|
+
### ${opt.name}
|
|
874
|
+
|
|
875
|
+
${opt.description}
|
|
876
|
+
|
|
877
|
+
**Pros:**
|
|
878
|
+
${opt.pros.map(p => `- ${p}`).join('\n')}
|
|
879
|
+
|
|
880
|
+
**Cons:**
|
|
881
|
+
${opt.cons.map(c => `- ${c}`).join('\n')}
|
|
882
|
+
|
|
883
|
+
**Effort:** ${opt.effort}
|
|
884
|
+
**Confidence:** ${opt.confidence}%
|
|
885
|
+
`).join('\n')}
|
|
886
|
+
|
|
887
|
+
## Consequences
|
|
888
|
+
|
|
889
|
+
### Technical Impacts
|
|
890
|
+
${session.context.impacts?.technical.map(i => `- ${i}`).join('\n')}
|
|
891
|
+
|
|
892
|
+
### Business Impacts
|
|
893
|
+
${session.context.impacts?.business.map(i => `- ${i}`).join('\n')}
|
|
894
|
+
|
|
895
|
+
### Team Impacts
|
|
896
|
+
${session.context.impacts?.team.map(i => `- ${i}`).join('\n')}
|
|
897
|
+
|
|
898
|
+
### Risks
|
|
899
|
+
${session.context.impacts?.risks.map(r => `- ${r}`).join('\n')}
|
|
900
|
+
|
|
901
|
+
### Dependencies
|
|
902
|
+
${session.context.impacts?.dependencies.map(d => `- ${d}`).join('\n')}
|
|
903
|
+
|
|
904
|
+
## Implementation Plan
|
|
905
|
+
|
|
906
|
+
### Phases
|
|
907
|
+
${session.context.implementation?.phases.map((p, i) => `${i + 1}. ${p}`).join('\n')}
|
|
908
|
+
|
|
909
|
+
### Tasks
|
|
910
|
+
${session.context.implementation?.tasks.map(t => `- [ ] ${t.description} (Priority: ${t.priority}, Effort: ${t.effort}${t.assignee ? `, Assignee: ${t.assignee}` : ''})`).join('\n')}
|
|
911
|
+
|
|
912
|
+
### Timeline
|
|
913
|
+
${session.context.implementation?.timeline}
|
|
914
|
+
|
|
915
|
+
### Success Criteria
|
|
916
|
+
${session.context.implementation?.successCriteria.map(c => `- ${c}`).join('\n')}
|
|
917
|
+
|
|
918
|
+
## References
|
|
919
|
+
|
|
920
|
+
- [Link to relevant documentation]
|
|
921
|
+
- [Link to research materials]
|
|
922
|
+
- [Link to architectural diagrams]
|
|
923
|
+
|
|
924
|
+
## Revision History
|
|
925
|
+
|
|
926
|
+
- ${date}: Initial draft created through interactive planning session
|
|
927
|
+
`;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Helper function to get next ADR number
|
|
931
|
+
*/
|
|
932
|
+
async function getNextAdrNumber(adrDir) {
|
|
933
|
+
try {
|
|
934
|
+
const files = await fs.readdir(adrDir);
|
|
935
|
+
const adrFiles = files.filter(f => f.match(/^\d{4}-.*\.md$/));
|
|
936
|
+
if (adrFiles.length === 0)
|
|
937
|
+
return 1;
|
|
938
|
+
const numbers = adrFiles.map(f => parseInt(f.substring(0, 4)));
|
|
939
|
+
return Math.max(...numbers) + 1;
|
|
940
|
+
}
|
|
941
|
+
catch {
|
|
942
|
+
return 1;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Helper function to calculate session progress
|
|
947
|
+
*/
|
|
948
|
+
function getSessionProgress(session) {
|
|
949
|
+
const phases = [
|
|
950
|
+
'problem_definition',
|
|
951
|
+
'research_analysis',
|
|
952
|
+
'option_exploration',
|
|
953
|
+
'decision_making',
|
|
954
|
+
'impact_assessment',
|
|
955
|
+
'implementation_planning',
|
|
956
|
+
'adr_generation'
|
|
957
|
+
];
|
|
958
|
+
const currentIndex = phases.indexOf(session.phase);
|
|
959
|
+
if (currentIndex === -1)
|
|
960
|
+
return 100; // completed
|
|
961
|
+
return Math.round((currentIndex / phases.length) * 100);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Helper function to calculate duration
|
|
965
|
+
*/
|
|
966
|
+
function calculateDuration(start, end) {
|
|
967
|
+
const startTime = new Date(start).getTime();
|
|
968
|
+
const endTime = new Date(end).getTime();
|
|
969
|
+
const duration = endTime - startTime;
|
|
970
|
+
const hours = Math.floor(duration / (1000 * 60 * 60));
|
|
971
|
+
const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60));
|
|
972
|
+
if (hours > 0) {
|
|
973
|
+
return `${hours}h ${minutes}m`;
|
|
974
|
+
}
|
|
975
|
+
return `${minutes}m`;
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Main entry point for the interactive ADR planning tool
|
|
979
|
+
*/
|
|
980
|
+
export async function interactiveAdrPlanning(args) {
|
|
981
|
+
try {
|
|
982
|
+
const input = InteractiveAdrPlanningSchema.parse(args);
|
|
983
|
+
switch (input.operation) {
|
|
984
|
+
case 'start_session':
|
|
985
|
+
return await startSession(input);
|
|
986
|
+
case 'continue_session':
|
|
987
|
+
return await continueSession(input);
|
|
988
|
+
case 'provide_input':
|
|
989
|
+
return await provideInput(input);
|
|
990
|
+
case 'request_research':
|
|
991
|
+
return await requestResearch(input);
|
|
992
|
+
case 'evaluate_options':
|
|
993
|
+
return await evaluateOptions(input);
|
|
994
|
+
case 'make_decision':
|
|
995
|
+
input.input = { selectedOption: input.input };
|
|
996
|
+
return await provideInput(input);
|
|
997
|
+
case 'assess_impact':
|
|
998
|
+
input.input = { impacts: input.input };
|
|
999
|
+
return await provideInput(input);
|
|
1000
|
+
case 'plan_implementation':
|
|
1001
|
+
input.input = { implementation: input.input };
|
|
1002
|
+
return await provideInput(input);
|
|
1003
|
+
case 'generate_adr':
|
|
1004
|
+
return await provideInput(input);
|
|
1005
|
+
case 'update_todos':
|
|
1006
|
+
return await updateTodos(input);
|
|
1007
|
+
case 'get_guidance':
|
|
1008
|
+
return await getGuidance(input);
|
|
1009
|
+
case 'save_session':
|
|
1010
|
+
const session = sessions.get(input.sessionId);
|
|
1011
|
+
if (!session)
|
|
1012
|
+
throw new McpAdrError('Session not found', 'SESSION_NOT_FOUND');
|
|
1013
|
+
await saveSession(session);
|
|
1014
|
+
return { success: true, message: 'Session saved' };
|
|
1015
|
+
case 'complete_session':
|
|
1016
|
+
return await completeSession(input);
|
|
1017
|
+
default:
|
|
1018
|
+
throw new McpAdrError(`Unknown operation: ${input.operation}`, 'INVALID_OPERATION');
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
catch (error) {
|
|
1022
|
+
if (error instanceof McpAdrError) {
|
|
1023
|
+
throw error;
|
|
1024
|
+
}
|
|
1025
|
+
throw new McpAdrError(`Interactive ADR planning failed: ${error instanceof Error ? error.message : String(error)}`, 'PLANNING_ERROR');
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
//# sourceMappingURL=interactive-adr-planning-tool.js.map
|