sdd-mcp-server 1.4.5 → 1.5.1
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 +4 -2
- package/dist/adapters/cli/SDDToolAdapter.d.ts +12 -9
- package/dist/adapters/cli/SDDToolAdapter.js +343 -227
- package/dist/adapters/cli/SDDToolAdapter.js.map +1 -1
- package/dist/application/services/RequirementsClarificationService.d.ts +67 -0
- package/dist/application/services/RequirementsClarificationService.js +486 -0
- package/dist/application/services/RequirementsClarificationService.js.map +1 -0
- package/dist/application/services/clarification-constants.d.ts +54 -0
- package/dist/application/services/clarification-constants.js +106 -0
- package/dist/application/services/clarification-constants.js.map +1 -0
- package/dist/domain/types.d.ts +58 -0
- package/dist/domain/types.js +8 -0
- package/dist/domain/types.js.map +1 -1
- package/dist/infrastructure/di/container.d.ts +2 -2
- package/dist/infrastructure/di/container.js +102 -60
- package/dist/infrastructure/di/container.js.map +1 -1
- package/dist/infrastructure/di/types.d.ts +1 -0
- package/dist/infrastructure/di/types.js +39 -38
- package/dist/infrastructure/di/types.js.map +1 -1
- package/mcp-server.js +1992 -1330
- package/package.json +1 -1
package/mcp-server.js
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import fs from
|
|
6
|
-
import path from
|
|
7
|
-
import {
|
|
8
|
-
analyzeProject,
|
|
9
|
-
generateProductDocument,
|
|
10
|
-
generateTechDocument,
|
|
11
|
-
generateStructureDocument
|
|
12
|
-
} from
|
|
5
|
+
import fs from "fs/promises";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import {
|
|
8
|
+
analyzeProject,
|
|
9
|
+
generateProductDocument,
|
|
10
|
+
generateTechDocument,
|
|
11
|
+
generateStructureDocument,
|
|
12
|
+
} from "./documentGenerator.js";
|
|
13
13
|
|
|
14
14
|
// Best-effort dynamic loader for spec generators (requirements/design/tasks)
|
|
15
15
|
async function loadSpecGenerator() {
|
|
16
16
|
const tried = [];
|
|
17
17
|
const attempts = [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
"./specGenerator.js", // root-level JS (dev/runtime)
|
|
19
|
+
"./dist/utils/specGenerator.js", // compiled TS output
|
|
20
|
+
"./utils/specGenerator.js", // TS runtime (when transpiled on-the-fly)
|
|
21
21
|
];
|
|
22
22
|
for (const p of attempts) {
|
|
23
23
|
try {
|
|
@@ -28,46 +28,52 @@ async function loadSpecGenerator() {
|
|
|
28
28
|
tried.push(`${p}: ${(e && e.message) || e}`);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
throw new Error(
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Unable to load specGenerator from known paths. Tried: \n- ${tried.join("\n- ")}`,
|
|
33
|
+
);
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
// Resolve version dynamically from package.json when possible
|
|
35
37
|
async function resolveVersion() {
|
|
36
38
|
try {
|
|
37
|
-
const pkgUrl = new URL(
|
|
38
|
-
const pkgText = await fs.readFile(pkgUrl,
|
|
39
|
+
const pkgUrl = new URL("./package.json", import.meta.url);
|
|
40
|
+
const pkgText = await fs.readFile(pkgUrl, "utf8");
|
|
39
41
|
const pkg = JSON.parse(pkgText);
|
|
40
|
-
return pkg.version ||
|
|
42
|
+
return pkg.version || "0.0.0";
|
|
41
43
|
} catch {
|
|
42
|
-
return
|
|
44
|
+
return "0.0.0";
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
const server = new McpServer(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
const server = new McpServer(
|
|
49
|
+
{
|
|
50
|
+
name: "sdd-mcp-server",
|
|
51
|
+
version: await resolveVersion(),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
instructions: "Use this server for spec-driven development workflows",
|
|
55
|
+
},
|
|
56
|
+
);
|
|
52
57
|
|
|
53
58
|
// Helper functions for file operations
|
|
54
59
|
async function createKiroDirectory(projectPath) {
|
|
55
|
-
const kiroPath = path.join(projectPath,
|
|
56
|
-
const specsPath = path.join(kiroPath,
|
|
57
|
-
const steeringPath = path.join(kiroPath,
|
|
58
|
-
|
|
60
|
+
const kiroPath = path.join(projectPath, ".kiro");
|
|
61
|
+
const specsPath = path.join(kiroPath, "specs");
|
|
62
|
+
const steeringPath = path.join(kiroPath, "steering");
|
|
63
|
+
|
|
59
64
|
await fs.mkdir(kiroPath, { recursive: true });
|
|
60
65
|
await fs.mkdir(specsPath, { recursive: true });
|
|
61
66
|
await fs.mkdir(steeringPath, { recursive: true });
|
|
62
|
-
|
|
67
|
+
|
|
63
68
|
return { kiroPath, specsPath, steeringPath };
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
async function generateFeatureName(description) {
|
|
67
72
|
// Simple feature name generation from description
|
|
68
|
-
return description
|
|
69
|
-
.
|
|
70
|
-
.replace(
|
|
73
|
+
return description
|
|
74
|
+
.toLowerCase()
|
|
75
|
+
.replace(/[^a-z0-9\s]/g, "")
|
|
76
|
+
.replace(/\s+/g, "-")
|
|
71
77
|
.substring(0, 50);
|
|
72
78
|
}
|
|
73
79
|
|
|
@@ -75,277 +81,637 @@ async function getCurrentTimestamp() {
|
|
|
75
81
|
return new Date().toISOString();
|
|
76
82
|
}
|
|
77
83
|
|
|
84
|
+
// Requirements Clarification helpers
|
|
85
|
+
function analyzeDescriptionQuality(description) {
|
|
86
|
+
const analysis = {
|
|
87
|
+
hasWhy:
|
|
88
|
+
/\b(problem|solve[sd]?|challenge|pain\s+point|issue|why|because|enables?|value\s+proposition|benefit|justification|goal|objective|purpose)\b/i.test(
|
|
89
|
+
description,
|
|
90
|
+
),
|
|
91
|
+
hasWho:
|
|
92
|
+
/\b(user[s]?|customer[s]?|target\s+(audience|users?|customers?)|persona[s]?|stakeholder[s]?|(developer|designer|admin|manager)[s]?|for\s+(teams?|companies|individuals))\b/i.test(
|
|
93
|
+
description,
|
|
94
|
+
),
|
|
95
|
+
hasWhat:
|
|
96
|
+
/\b(feature[s]?|functionality|capabilit(y|ies)|provides?|include[s]?|support[s]?|allow[s]?|enable[s]?|MVP|scope)\b/i.test(
|
|
97
|
+
description,
|
|
98
|
+
),
|
|
99
|
+
hasSuccessCriteria:
|
|
100
|
+
/\b(metric[s]?|KPI[s]?|measure[sd]?|success\s+(criteria|metric)|\d+%|performance\s+target|goal[s]?\s+.*\d+)\b/i.test(
|
|
101
|
+
description,
|
|
102
|
+
),
|
|
103
|
+
ambiguousTerms: [],
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Detect ambiguous terms
|
|
107
|
+
const ambiguityChecks = [
|
|
108
|
+
{
|
|
109
|
+
pattern: /\bfast\b/gi,
|
|
110
|
+
suggestion: "Can you specify a target response time?",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
pattern: /\bscalable\b/gi,
|
|
114
|
+
suggestion: "What scale do you need to support?",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
pattern: /\buser[- ]friendly\b/gi,
|
|
118
|
+
suggestion: "What makes it user-friendly?",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
pattern: /\beasy\s+to\s+use\b/gi,
|
|
122
|
+
suggestion: "What does easy mean for your users?",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
pattern: /\bhigh\s+quality\b/gi,
|
|
126
|
+
suggestion: "How do you define quality?",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
pattern: /\breliable\b/gi,
|
|
130
|
+
suggestion: "What reliability level do you need?",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
pattern: /\bsecure\b/gi,
|
|
134
|
+
suggestion: "What security requirements apply?",
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
pattern: /\bmodern\b/gi,
|
|
138
|
+
suggestion: "What technologies are you considering?",
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
for (const { pattern, suggestion } of ambiguityChecks) {
|
|
143
|
+
const matches = description.match(pattern);
|
|
144
|
+
if (matches) {
|
|
145
|
+
analysis.ambiguousTerms.push({ term: matches[0], suggestion });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Calculate quality score
|
|
150
|
+
let score = 0;
|
|
151
|
+
if (analysis.hasWhy) score += 30;
|
|
152
|
+
if (analysis.hasWho) score += 20;
|
|
153
|
+
if (analysis.hasWhat) score += 20;
|
|
154
|
+
if (analysis.hasSuccessCriteria) score += 15;
|
|
155
|
+
if (description.length > 100) score += 5;
|
|
156
|
+
if (description.length > 300) score += 5;
|
|
157
|
+
if (description.length > 500) score += 5;
|
|
158
|
+
score -= Math.min(15, analysis.ambiguousTerms.length * 5);
|
|
159
|
+
|
|
160
|
+
analysis.qualityScore = Math.max(0, Math.min(100, score));
|
|
161
|
+
analysis.needsClarification = score < 70;
|
|
162
|
+
|
|
163
|
+
return analysis;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function generateClarificationQuestions(analysis) {
|
|
167
|
+
const questions = [];
|
|
168
|
+
|
|
169
|
+
if (!analysis.hasWhy) {
|
|
170
|
+
questions.push({
|
|
171
|
+
id: "why_problem",
|
|
172
|
+
category: "why",
|
|
173
|
+
question:
|
|
174
|
+
"What business problem does this project solve? Why is it needed?",
|
|
175
|
+
examples: [
|
|
176
|
+
"Our customer support team spends 5 hours/day on repetitive inquiries",
|
|
177
|
+
"Users are abandoning checkout because the process takes too long",
|
|
178
|
+
],
|
|
179
|
+
required: true,
|
|
180
|
+
});
|
|
181
|
+
questions.push({
|
|
182
|
+
id: "why_value",
|
|
183
|
+
category: "why",
|
|
184
|
+
question:
|
|
185
|
+
"What value does this project provide to users or the business?",
|
|
186
|
+
examples: [
|
|
187
|
+
"Reduce support ticket volume by 40%",
|
|
188
|
+
"Increase conversion rate by improving checkout speed",
|
|
189
|
+
],
|
|
190
|
+
required: true,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!analysis.hasWho) {
|
|
195
|
+
questions.push({
|
|
196
|
+
id: "who_users",
|
|
197
|
+
category: "who",
|
|
198
|
+
question: "Who are the primary users of this project?",
|
|
199
|
+
examples: [
|
|
200
|
+
"Customer support agents using ticketing systems",
|
|
201
|
+
"E-commerce shoppers on mobile devices",
|
|
202
|
+
"Backend developers integrating APIs",
|
|
203
|
+
],
|
|
204
|
+
required: true,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!analysis.hasWhat) {
|
|
209
|
+
questions.push({
|
|
210
|
+
id: "what_features",
|
|
211
|
+
category: "what",
|
|
212
|
+
question: "What are the 3-5 core features for the MVP?",
|
|
213
|
+
examples: [
|
|
214
|
+
"Auto-response system, ticket categorization, analytics dashboard",
|
|
215
|
+
"Product search, cart management, payment integration",
|
|
216
|
+
],
|
|
217
|
+
required: true,
|
|
218
|
+
});
|
|
219
|
+
questions.push({
|
|
220
|
+
id: "what_scope",
|
|
221
|
+
category: "what",
|
|
222
|
+
question: "What is explicitly OUT OF SCOPE for this project?",
|
|
223
|
+
examples: ["Admin panel (future phase)", "Mobile app (web only for MVP)"],
|
|
224
|
+
required: false,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!analysis.hasSuccessCriteria) {
|
|
229
|
+
questions.push({
|
|
230
|
+
id: "success_metrics",
|
|
231
|
+
category: "success",
|
|
232
|
+
question: "How will you measure if this project is successful?",
|
|
233
|
+
examples: [
|
|
234
|
+
"Support ticket volume reduced by 30% within 3 months",
|
|
235
|
+
"Page load time under 2 seconds, conversion rate > 3%",
|
|
236
|
+
],
|
|
237
|
+
required: true,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Add questions for ambiguous terms (max 2)
|
|
242
|
+
for (const ambiguous of analysis.ambiguousTerms.slice(0, 2)) {
|
|
243
|
+
questions.push({
|
|
244
|
+
id: `ambiguous_${ambiguous.term.toLowerCase().replace(/\s+/g, "_")}`,
|
|
245
|
+
category: "what",
|
|
246
|
+
question: `You mentioned "${ambiguous.term}". ${ambiguous.suggestion}`,
|
|
247
|
+
required: false,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return questions;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function formatQuestionsForUser(questions) {
|
|
255
|
+
let output = "## Requirements Clarification Needed\n\n";
|
|
256
|
+
output +=
|
|
257
|
+
"Your project description needs more detail to ensure we build the right solution.\n\n";
|
|
258
|
+
output += `**Quality Score**: ${Math.round(questions.analysis?.qualityScore || 0)}/100 (need 70+ to proceed)\n\n`;
|
|
259
|
+
output += "### Please answer these questions:\n\n";
|
|
260
|
+
|
|
261
|
+
let questionNum = 1;
|
|
262
|
+
for (const q of questions) {
|
|
263
|
+
output += `**${questionNum}. ${q.question}**${q.required ? " *(required)*" : ""}\n`;
|
|
264
|
+
if (q.examples && q.examples.length > 0) {
|
|
265
|
+
output += ` Examples:\n`;
|
|
266
|
+
for (const ex of q.examples) {
|
|
267
|
+
output += ` - ${ex}\n`;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
output += ` Answer ID: \`${q.id}\`\n\n`;
|
|
271
|
+
questionNum++;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
output += "\n### How to Provide Answers\n\n";
|
|
275
|
+
output +=
|
|
276
|
+
"Call sdd-init again with the `clarificationAnswers` parameter:\n\n";
|
|
277
|
+
output += "```json\n{\n";
|
|
278
|
+
output += ' "projectName": "your-project-name",\n';
|
|
279
|
+
output += ' "description": "your original description",\n';
|
|
280
|
+
output += ' "clarificationAnswers": {\n';
|
|
281
|
+
for (let i = 0; i < questions.length; i++) {
|
|
282
|
+
const q = questions[i];
|
|
283
|
+
output += ` "${q.id}": "your answer here"${i < questions.length - 1 ? "," : ""}\n`;
|
|
284
|
+
}
|
|
285
|
+
output += " }\n}\n```\n";
|
|
286
|
+
|
|
287
|
+
return output;
|
|
288
|
+
}
|
|
289
|
+
|
|
78
290
|
// Register all SDD tools matching README and kiro command templates
|
|
79
291
|
|
|
80
|
-
// 1. sdd-init - Initialize new SDD project
|
|
81
|
-
server.registerTool(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
292
|
+
// 1. sdd-init - Initialize new SDD project with interactive clarification
|
|
293
|
+
server.registerTool(
|
|
294
|
+
"sdd-init",
|
|
295
|
+
{
|
|
296
|
+
title: "Initialize SDD Project",
|
|
297
|
+
description:
|
|
298
|
+
"Initialize a new SDD project with interactive requirements clarification",
|
|
299
|
+
inputSchema: {
|
|
300
|
+
projectName: z.string().describe("The name of the project to initialize"),
|
|
301
|
+
description: z.string().optional().describe("Project description"),
|
|
302
|
+
clarificationAnswers: z
|
|
303
|
+
.record(z.string())
|
|
304
|
+
.optional()
|
|
305
|
+
.describe("Answers to clarification questions (second pass)"),
|
|
306
|
+
},
|
|
87
307
|
},
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
308
|
+
async ({ projectName, description = "", clarificationAnswers }) => {
|
|
309
|
+
try {
|
|
310
|
+
const currentPath = process.cwd();
|
|
311
|
+
|
|
312
|
+
// FIRST PASS: Analyze description quality and generate clarification questions if needed
|
|
313
|
+
if (!clarificationAnswers) {
|
|
314
|
+
const analysis = analyzeDescriptionQuality(description);
|
|
315
|
+
|
|
316
|
+
// If description is vague or incomplete, BLOCK and ask for clarification
|
|
317
|
+
if (analysis.needsClarification) {
|
|
318
|
+
const questions = generateClarificationQuestions(analysis);
|
|
319
|
+
questions.analysis = analysis; // Attach analysis for formatting
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
content: [
|
|
323
|
+
{
|
|
324
|
+
type: "text",
|
|
325
|
+
text: formatQuestionsForUser(questions),
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// SECOND PASS: Validate answers if provided
|
|
333
|
+
if (clarificationAnswers) {
|
|
334
|
+
const analysis = analyzeDescriptionQuality(description);
|
|
335
|
+
const questions = generateClarificationQuestions(analysis);
|
|
336
|
+
|
|
337
|
+
// Validate required questions are answered
|
|
338
|
+
const missingRequired = questions
|
|
339
|
+
.filter((q) => q.required && !clarificationAnswers[q.id])
|
|
340
|
+
.map((q) => q.question);
|
|
341
|
+
|
|
342
|
+
if (missingRequired.length > 0) {
|
|
343
|
+
return {
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: "text",
|
|
347
|
+
text: `## Missing Required Answers\n\nThe following required questions were not answered:\n\n${missingRequired.map((q, i) => `${i + 1}. ${q}`).join("\n")}\n\nPlease provide answers for all required questions.`,
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Build enriched description from answers
|
|
354
|
+
const whyAnswers = questions
|
|
355
|
+
.filter((q) => q.category === "why")
|
|
356
|
+
.map((q) => clarificationAnswers[q.id])
|
|
357
|
+
.filter((a) => a)
|
|
358
|
+
.join(" ");
|
|
359
|
+
|
|
360
|
+
const whoAnswers = questions
|
|
361
|
+
.filter((q) => q.category === "who")
|
|
362
|
+
.map((q) => clarificationAnswers[q.id])
|
|
363
|
+
.filter((a) => a)
|
|
364
|
+
.join(" ");
|
|
365
|
+
|
|
366
|
+
const whatAnswers = questions
|
|
367
|
+
.filter((q) => q.category === "what")
|
|
368
|
+
.map((q) => clarificationAnswers[q.id])
|
|
369
|
+
.filter((a) => a)
|
|
370
|
+
.join(" ");
|
|
371
|
+
|
|
372
|
+
const successAnswers = questions
|
|
373
|
+
.filter((q) => q.category === "success")
|
|
374
|
+
.map((q) => clarificationAnswers[q.id])
|
|
375
|
+
.filter((a) => a)
|
|
376
|
+
.join(" ");
|
|
377
|
+
|
|
378
|
+
// Synthesize enriched description
|
|
379
|
+
const enrichedParts = [];
|
|
380
|
+
if (description)
|
|
381
|
+
enrichedParts.push(`## Original Description\n${description}`);
|
|
382
|
+
if (whyAnswers)
|
|
383
|
+
enrichedParts.push(`## Business Justification (Why)\n${whyAnswers}`);
|
|
384
|
+
if (whoAnswers)
|
|
385
|
+
enrichedParts.push(`## Target Users (Who)\n${whoAnswers}`);
|
|
386
|
+
if (whatAnswers)
|
|
387
|
+
enrichedParts.push(`## Core Features (What)\n${whatAnswers}`);
|
|
388
|
+
if (successAnswers)
|
|
389
|
+
enrichedParts.push(`## Success Criteria\n${successAnswers}`);
|
|
390
|
+
|
|
391
|
+
description = enrichedParts.join("\n\n");
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Proceed with spec creation (either quality is good or answers were provided)
|
|
395
|
+
// Create .kiro directory structure in current directory (not in a subdirectory)
|
|
396
|
+
const { specsPath, steeringPath } =
|
|
397
|
+
await createKiroDirectory(currentPath);
|
|
398
|
+
|
|
399
|
+
// Generate feature name from description
|
|
400
|
+
const featureName = await generateFeatureName(description || projectName);
|
|
401
|
+
const featurePath = path.join(specsPath, featureName);
|
|
402
|
+
await fs.mkdir(featurePath, { recursive: true });
|
|
403
|
+
|
|
404
|
+
// Create spec.json
|
|
405
|
+
const specJson = {
|
|
406
|
+
feature_name: featureName,
|
|
407
|
+
created_at: await getCurrentTimestamp(),
|
|
408
|
+
updated_at: await getCurrentTimestamp(),
|
|
409
|
+
language: "en",
|
|
410
|
+
phase: "initialized",
|
|
411
|
+
approvals: {
|
|
412
|
+
requirements: {
|
|
413
|
+
generated: false,
|
|
414
|
+
approved: false,
|
|
415
|
+
},
|
|
416
|
+
design: {
|
|
417
|
+
generated: false,
|
|
418
|
+
approved: false,
|
|
419
|
+
},
|
|
420
|
+
tasks: {
|
|
421
|
+
generated: false,
|
|
422
|
+
approved: false,
|
|
423
|
+
},
|
|
115
424
|
},
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
425
|
+
ready_for_implementation: false,
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
await fs.writeFile(
|
|
429
|
+
path.join(featurePath, "spec.json"),
|
|
430
|
+
JSON.stringify(specJson, null, 2),
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
// Create requirements.md template
|
|
434
|
+
const requirementsTemplate = `# Requirements Document\n\n## Project Description (Input)\n${description}\n\n## Requirements\n<!-- Will be generated in /kiro:spec-requirements phase -->`;
|
|
435
|
+
await fs.writeFile(
|
|
436
|
+
path.join(featurePath, "requirements.md"),
|
|
437
|
+
requirementsTemplate,
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
// Ensure AGENTS.md exists in steering directory based on CLAUDE.md (static exception)
|
|
441
|
+
const agentsPath = path.join(steeringPath, "AGENTS.md");
|
|
442
|
+
const claudePath = path.join(currentPath, "CLAUDE.md");
|
|
443
|
+
const agentsExists = await fs
|
|
444
|
+
.access(agentsPath)
|
|
445
|
+
.then(() => true)
|
|
446
|
+
.catch(() => false);
|
|
447
|
+
if (!agentsExists) {
|
|
448
|
+
let agentsContent = "";
|
|
449
|
+
const claudeExists = await fs
|
|
450
|
+
.access(claudePath)
|
|
451
|
+
.then(() => true)
|
|
452
|
+
.catch(() => false);
|
|
453
|
+
if (claudeExists) {
|
|
454
|
+
const claude = await fs.readFile(claudePath, "utf8");
|
|
455
|
+
agentsContent = claude
|
|
456
|
+
.replace(
|
|
457
|
+
/# Claude Code Spec-Driven Development/g,
|
|
458
|
+
"# AI Agent Spec-Driven Development",
|
|
459
|
+
)
|
|
460
|
+
.replace(/Claude Code/g, "AI Agent")
|
|
461
|
+
.replace(/claude code/g, "ai agent")
|
|
462
|
+
.replace(/Claude/g, "AI Agent")
|
|
463
|
+
.replace(/claude/g, "ai agent");
|
|
464
|
+
} else {
|
|
465
|
+
agentsContent =
|
|
466
|
+
"# AI Agent Spec-Driven Development\n\nKiro-style Spec Driven Development implementation for AI agents across different CLIs and IDEs.";
|
|
119
467
|
}
|
|
120
|
-
|
|
121
|
-
"ready_for_implementation": false
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
await fs.writeFile(path.join(featurePath, 'spec.json'), JSON.stringify(specJson, null, 2));
|
|
125
|
-
|
|
126
|
-
// Create requirements.md template
|
|
127
|
-
const requirementsTemplate = `# Requirements Document\n\n## Project Description (Input)\n${description}\n\n## Requirements\n<!-- Will be generated in /kiro:spec-requirements phase -->`;
|
|
128
|
-
await fs.writeFile(path.join(featurePath, 'requirements.md'), requirementsTemplate);
|
|
129
|
-
|
|
130
|
-
// Ensure AGENTS.md exists in steering directory based on CLAUDE.md (static exception)
|
|
131
|
-
const agentsPath = path.join(steeringPath, 'AGENTS.md');
|
|
132
|
-
const claudePath = path.join(currentPath, 'CLAUDE.md');
|
|
133
|
-
const agentsExists = await fs.access(agentsPath).then(() => true).catch(() => false);
|
|
134
|
-
if (!agentsExists) {
|
|
135
|
-
let agentsContent = '';
|
|
136
|
-
const claudeExists = await fs.access(claudePath).then(() => true).catch(() => false);
|
|
137
|
-
if (claudeExists) {
|
|
138
|
-
const claude = await fs.readFile(claudePath, 'utf8');
|
|
139
|
-
agentsContent = claude
|
|
140
|
-
.replace(/# Claude Code Spec-Driven Development/g, '# AI Agent Spec-Driven Development')
|
|
141
|
-
.replace(/Claude Code/g, 'AI Agent')
|
|
142
|
-
.replace(/claude code/g, 'ai agent')
|
|
143
|
-
.replace(/Claude/g, 'AI Agent')
|
|
144
|
-
.replace(/claude/g, 'ai agent');
|
|
145
|
-
} else {
|
|
146
|
-
agentsContent = '# AI Agent Spec-Driven Development\n\nKiro-style Spec Driven Development implementation for AI agents across different CLIs and IDEs.';
|
|
468
|
+
await fs.writeFile(agentsPath, agentsContent);
|
|
147
469
|
}
|
|
148
|
-
await fs.writeFile(agentsPath, agentsContent);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
content: [{
|
|
153
|
-
type: 'text',
|
|
154
|
-
text: `## Spec Initialization Complete\n\n**Generated Feature Name**: ${featureName}\n**Project Name**: ${projectName}\n**Project Description**: ${description}\n\n**Created Files**:\n- \`.kiro/specs/${featureName}/spec.json\` - Metadata and approval tracking\n- \`.kiro/specs/${featureName}/requirements.md\` - Requirements template\n\n**Next Step**: Use \`sdd-requirements ${featureName}\` to generate comprehensive requirements\n\nThe spec has been initialized following stage-by-stage development principles.`
|
|
155
|
-
}]
|
|
156
|
-
};
|
|
157
|
-
} catch (error) {
|
|
158
|
-
return {
|
|
159
|
-
content: [{
|
|
160
|
-
type: 'text',
|
|
161
|
-
text: `Error initializing SDD project: ${error.message}`
|
|
162
|
-
}]
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
470
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
inputSchema: {
|
|
172
|
-
featureName: z.string().describe('Feature name from spec initialization')
|
|
173
|
-
},
|
|
174
|
-
}, async ({ featureName }) => {
|
|
175
|
-
try {
|
|
176
|
-
const currentPath = process.cwd();
|
|
177
|
-
const featurePath = path.join(currentPath, '.kiro', 'specs', featureName);
|
|
178
|
-
|
|
179
|
-
// Check if spec exists
|
|
180
|
-
const specPath = path.join(featurePath, 'spec.json');
|
|
181
|
-
const specExists = await fs.access(specPath).then(() => true).catch(() => false);
|
|
182
|
-
if (!specExists) {
|
|
471
|
+
const clarificationNote = clarificationAnswers
|
|
472
|
+
? "\n\n✅ **Requirements Clarification**: Your answers have been incorporated into an enriched project description."
|
|
473
|
+
: "";
|
|
474
|
+
|
|
183
475
|
return {
|
|
184
|
-
content: [
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
476
|
+
content: [
|
|
477
|
+
{
|
|
478
|
+
type: "text",
|
|
479
|
+
text: `## Spec Initialization Complete\n\n**Generated Feature Name**: ${featureName}\n**Project Name**: ${projectName}\n**Project Description**: ${description}${clarificationNote}\n\n**Created Files**:\n- \`.kiro/specs/${featureName}/spec.json\` - Metadata and approval tracking\n- \`.kiro/specs/${featureName}/requirements.md\` - Requirements template with enriched description\n\n**Next Step**: Use \`sdd-requirements ${featureName}\` to generate comprehensive requirements\n\nThe spec has been initialized following stage-by-stage development principles.`,
|
|
480
|
+
},
|
|
481
|
+
],
|
|
482
|
+
};
|
|
483
|
+
} catch (error) {
|
|
484
|
+
return {
|
|
485
|
+
content: [
|
|
486
|
+
{
|
|
487
|
+
type: "text",
|
|
488
|
+
text: `Error initializing SDD project: ${error.message}`,
|
|
489
|
+
},
|
|
490
|
+
],
|
|
188
491
|
};
|
|
189
492
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const specContent = await fs.readFile(specPath, 'utf8');
|
|
193
|
-
const spec = JSON.parse(specContent);
|
|
194
|
-
|
|
195
|
-
// Generate requirements using specGenerator with fallback
|
|
196
|
-
let requirementsContent;
|
|
197
|
-
try {
|
|
198
|
-
const { mod } = await loadSpecGenerator();
|
|
199
|
-
requirementsContent = await mod.generateRequirementsDocument(currentPath, featureName);
|
|
200
|
-
} catch (e) {
|
|
201
|
-
requirementsContent = `# Requirements Document\n\n<!-- Warning: Analysis-backed generation failed. Using fallback template. -->\n<!-- Error: ${e && e.message ? e.message : String(e)} -->\n\n## Project Context\n**Feature**: ${spec.feature_name}\n**Description**: ${spec.description || 'Feature to be implemented'}\n`;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
await fs.writeFile(path.join(featurePath, 'requirements.md'), requirementsContent);
|
|
205
|
-
|
|
206
|
-
// Update spec.json
|
|
207
|
-
spec.phase = "requirements-generated";
|
|
208
|
-
spec.approvals.requirements.generated = true;
|
|
209
|
-
spec.updated_at = await getCurrentTimestamp();
|
|
210
|
-
|
|
211
|
-
await fs.writeFile(specPath, JSON.stringify(spec, null, 2));
|
|
212
|
-
|
|
213
|
-
return {
|
|
214
|
-
content: [{
|
|
215
|
-
type: 'text',
|
|
216
|
-
text: `## Requirements Generated\n\nRequirements document generated for feature: **${featureName}**\n\n**Generated**: .kiro/specs/${featureName}/requirements.md\n**Status**: Requirements phase completed\n\n**Next Step**: Review the requirements, then use \`sdd-design\` to proceed to design phase`
|
|
217
|
-
}]
|
|
218
|
-
};
|
|
219
|
-
} catch (error) {
|
|
220
|
-
return {
|
|
221
|
-
content: [{
|
|
222
|
-
type: 'text',
|
|
223
|
-
text: `Error generating requirements: ${error.message}`
|
|
224
|
-
}]
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
});
|
|
493
|
+
},
|
|
494
|
+
);
|
|
228
495
|
|
|
229
|
-
//
|
|
230
|
-
server.registerTool(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
496
|
+
// 2. sdd-requirements - Generate requirements doc
|
|
497
|
+
server.registerTool(
|
|
498
|
+
"sdd-requirements",
|
|
499
|
+
{
|
|
500
|
+
title: "Generate Requirements Document",
|
|
501
|
+
description: "Generate requirements doc",
|
|
502
|
+
inputSchema: {
|
|
503
|
+
featureName: z.string().describe("Feature name from spec initialization"),
|
|
504
|
+
},
|
|
235
505
|
},
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
506
|
+
async ({ featureName }) => {
|
|
507
|
+
try {
|
|
508
|
+
const currentPath = process.cwd();
|
|
509
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
510
|
+
|
|
511
|
+
// Check if spec exists
|
|
512
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
513
|
+
const specExists = await fs
|
|
514
|
+
.access(specPath)
|
|
515
|
+
.then(() => true)
|
|
516
|
+
.catch(() => false);
|
|
517
|
+
if (!specExists) {
|
|
518
|
+
return {
|
|
519
|
+
content: [
|
|
520
|
+
{
|
|
521
|
+
type: "text",
|
|
522
|
+
text: `Error: Spec not found for feature "${featureName}". Use sdd-init first.`,
|
|
523
|
+
},
|
|
524
|
+
],
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Read existing spec
|
|
529
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
530
|
+
const spec = JSON.parse(specContent);
|
|
531
|
+
|
|
532
|
+
// Generate requirements using specGenerator with fallback
|
|
533
|
+
let requirementsContent;
|
|
534
|
+
try {
|
|
535
|
+
const { mod } = await loadSpecGenerator();
|
|
536
|
+
requirementsContent = await mod.generateRequirementsDocument(
|
|
537
|
+
currentPath,
|
|
538
|
+
featureName,
|
|
539
|
+
);
|
|
540
|
+
} catch (e) {
|
|
541
|
+
requirementsContent = `# Requirements Document\n\n<!-- Warning: Analysis-backed generation failed. Using fallback template. -->\n<!-- Error: ${e && e.message ? e.message : String(e)} -->\n\n## Project Context\n**Feature**: ${spec.feature_name}\n**Description**: ${spec.description || "Feature to be implemented"}\n`;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
await fs.writeFile(
|
|
545
|
+
path.join(featurePath, "requirements.md"),
|
|
546
|
+
requirementsContent,
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
// Update spec.json
|
|
550
|
+
spec.phase = "requirements-generated";
|
|
551
|
+
spec.approvals.requirements.generated = true;
|
|
552
|
+
spec.updated_at = await getCurrentTimestamp();
|
|
553
|
+
|
|
554
|
+
await fs.writeFile(specPath, JSON.stringify(spec, null, 2));
|
|
555
|
+
|
|
247
556
|
return {
|
|
248
|
-
content: [
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
557
|
+
content: [
|
|
558
|
+
{
|
|
559
|
+
type: "text",
|
|
560
|
+
text: `## Requirements Generated\n\nRequirements document generated for feature: **${featureName}**\n\n**Generated**: .kiro/specs/${featureName}/requirements.md\n**Status**: Requirements phase completed\n\n**Next Step**: Review the requirements, then use \`sdd-design\` to proceed to design phase`,
|
|
561
|
+
},
|
|
562
|
+
],
|
|
252
563
|
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Read requirements for context
|
|
256
|
-
const requirementsPath = path.join(featurePath, 'requirements.md');
|
|
257
|
-
let requirementsContext = '';
|
|
258
|
-
try {
|
|
259
|
-
requirementsContext = await fs.readFile(requirementsPath, 'utf8');
|
|
260
564
|
} catch (error) {
|
|
261
|
-
|
|
565
|
+
return {
|
|
566
|
+
content: [
|
|
567
|
+
{
|
|
568
|
+
type: "text",
|
|
569
|
+
text: `Error generating requirements: ${error.message}`,
|
|
570
|
+
},
|
|
571
|
+
],
|
|
572
|
+
};
|
|
262
573
|
}
|
|
574
|
+
},
|
|
575
|
+
);
|
|
263
576
|
|
|
264
|
-
|
|
265
|
-
|
|
577
|
+
// 3. sdd-design - Create design specifications
|
|
578
|
+
server.registerTool(
|
|
579
|
+
"sdd-design",
|
|
580
|
+
{
|
|
581
|
+
title: "Create Design Specifications",
|
|
582
|
+
description: "Create design specifications",
|
|
583
|
+
inputSchema: {
|
|
584
|
+
featureName: z.string().describe("Feature name from spec initialization"),
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
async ({ featureName }) => {
|
|
266
588
|
try {
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
589
|
+
const currentPath = process.cwd();
|
|
590
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
591
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
592
|
+
|
|
593
|
+
// Read and validate spec
|
|
594
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
595
|
+
const spec = JSON.parse(specContent);
|
|
596
|
+
|
|
597
|
+
if (!spec.approvals.requirements.generated) {
|
|
598
|
+
return {
|
|
599
|
+
content: [
|
|
600
|
+
{
|
|
601
|
+
type: "text",
|
|
602
|
+
text: `Error: Requirements must be generated before design. Run \`sdd-requirements ${featureName}\` first.`,
|
|
603
|
+
},
|
|
604
|
+
],
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Read requirements for context
|
|
609
|
+
const requirementsPath = path.join(featurePath, "requirements.md");
|
|
610
|
+
let requirementsContext = "";
|
|
611
|
+
try {
|
|
612
|
+
requirementsContext = await fs.readFile(requirementsPath, "utf8");
|
|
613
|
+
} catch (error) {
|
|
614
|
+
requirementsContext = "Requirements document not available";
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Generate design using specGenerator with fallback
|
|
618
|
+
let designContent;
|
|
619
|
+
try {
|
|
620
|
+
const { mod } = await loadSpecGenerator();
|
|
621
|
+
designContent = await mod.generateDesignDocument(
|
|
622
|
+
currentPath,
|
|
623
|
+
featureName,
|
|
624
|
+
);
|
|
625
|
+
} catch (e) {
|
|
626
|
+
designContent = `# Technical Design Document\n\n<!-- Warning: Analysis-backed generation failed. Using fallback template. -->\n<!-- Error: ${e && e.message ? e.message : String(e)} -->\n\n## Project Context\n**Feature**: ${spec.feature_name}\n**Phase**: ${spec.phase}`;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
await fs.writeFile(path.join(featurePath, "design.md"), designContent);
|
|
630
|
+
|
|
631
|
+
// Update spec.json
|
|
632
|
+
spec.phase = "design-generated";
|
|
633
|
+
spec.approvals.design.generated = true;
|
|
634
|
+
spec.updated_at = await getCurrentTimestamp();
|
|
635
|
+
|
|
636
|
+
await fs.writeFile(specPath, JSON.stringify(spec, null, 2));
|
|
297
637
|
|
|
298
|
-
// 4. sdd-tasks - Generate task breakdown
|
|
299
|
-
server.registerTool("sdd-tasks", {
|
|
300
|
-
title: "Generate Task Breakdown",
|
|
301
|
-
description: "Generate task breakdown",
|
|
302
|
-
inputSchema: {
|
|
303
|
-
featureName: z.string().describe('Feature name from spec initialization')
|
|
304
|
-
},
|
|
305
|
-
}, async ({ featureName }) => {
|
|
306
|
-
try {
|
|
307
|
-
const currentPath = process.cwd();
|
|
308
|
-
const featurePath = path.join(currentPath, '.kiro', 'specs', featureName);
|
|
309
|
-
const specPath = path.join(featurePath, 'spec.json');
|
|
310
|
-
|
|
311
|
-
// Read and validate spec
|
|
312
|
-
const specContent = await fs.readFile(specPath, 'utf8');
|
|
313
|
-
const spec = JSON.parse(specContent);
|
|
314
|
-
|
|
315
|
-
if (!spec.approvals.design.generated) {
|
|
316
638
|
return {
|
|
317
|
-
content: [
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
639
|
+
content: [
|
|
640
|
+
{
|
|
641
|
+
type: "text",
|
|
642
|
+
text: `## Design Generated\n\nTechnical design document generated for feature: **${featureName}**\n\n**Generated**: .kiro/specs/${featureName}/design.md\n**Status**: Design phase completed\n\n**Next Step**: Review the design document, then use \`sdd-tasks\` to proceed to task planning phase`,
|
|
643
|
+
},
|
|
644
|
+
],
|
|
321
645
|
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Read design and requirements for context
|
|
325
|
-
const designPath = path.join(featurePath, 'design.md');
|
|
326
|
-
const requirementsPath = path.join(featurePath, 'requirements.md');
|
|
327
|
-
let designContext = '';
|
|
328
|
-
let requirementsContext = '';
|
|
329
|
-
|
|
330
|
-
try {
|
|
331
|
-
designContext = await fs.readFile(designPath, 'utf8');
|
|
332
646
|
} catch (error) {
|
|
333
|
-
|
|
647
|
+
return {
|
|
648
|
+
content: [
|
|
649
|
+
{
|
|
650
|
+
type: "text",
|
|
651
|
+
text: `Error generating design: ${error.message}`,
|
|
652
|
+
},
|
|
653
|
+
],
|
|
654
|
+
};
|
|
334
655
|
}
|
|
656
|
+
},
|
|
657
|
+
);
|
|
335
658
|
|
|
659
|
+
// 4. sdd-tasks - Generate task breakdown
|
|
660
|
+
server.registerTool(
|
|
661
|
+
"sdd-tasks",
|
|
662
|
+
{
|
|
663
|
+
title: "Generate Task Breakdown",
|
|
664
|
+
description: "Generate task breakdown",
|
|
665
|
+
inputSchema: {
|
|
666
|
+
featureName: z.string().describe("Feature name from spec initialization"),
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
async ({ featureName }) => {
|
|
336
670
|
try {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
671
|
+
const currentPath = process.cwd();
|
|
672
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
673
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
674
|
+
|
|
675
|
+
// Read and validate spec
|
|
676
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
677
|
+
const spec = JSON.parse(specContent);
|
|
678
|
+
|
|
679
|
+
if (!spec.approvals.design.generated) {
|
|
680
|
+
return {
|
|
681
|
+
content: [
|
|
682
|
+
{
|
|
683
|
+
type: "text",
|
|
684
|
+
text: `Error: Design must be generated before tasks. Run \`sdd-design ${featureName}\` first.`,
|
|
685
|
+
},
|
|
686
|
+
],
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Read design and requirements for context
|
|
691
|
+
const designPath = path.join(featurePath, "design.md");
|
|
692
|
+
const requirementsPath = path.join(featurePath, "requirements.md");
|
|
693
|
+
let designContext = "";
|
|
694
|
+
let requirementsContext = "";
|
|
695
|
+
|
|
696
|
+
try {
|
|
697
|
+
designContext = await fs.readFile(designPath, "utf8");
|
|
698
|
+
} catch (error) {
|
|
699
|
+
designContext = "Design document not available";
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
try {
|
|
703
|
+
requirementsContext = await fs.readFile(requirementsPath, "utf8");
|
|
704
|
+
} catch (error) {
|
|
705
|
+
requirementsContext = "Requirements document not available";
|
|
706
|
+
}
|
|
341
707
|
|
|
342
|
-
|
|
343
|
-
|
|
708
|
+
// Generate tasks document based on requirements and design
|
|
709
|
+
const tasksContent = `# Implementation Plan
|
|
344
710
|
|
|
345
711
|
## Project Context
|
|
346
712
|
**Feature**: ${spec.feature_name}
|
|
347
|
-
**Description**: ${spec.description ||
|
|
348
|
-
**Design Phase**: ${spec.approvals.design.generated ?
|
|
713
|
+
**Description**: ${spec.description || "Feature to be implemented"}
|
|
714
|
+
**Design Phase**: ${spec.approvals.design.generated ? "Completed" : "Pending"}
|
|
349
715
|
|
|
350
716
|
## Instructions for AI Agent
|
|
351
717
|
|
|
@@ -370,12 +736,12 @@ Create tasks that:
|
|
|
370
736
|
|
|
371
737
|
## Requirements Context
|
|
372
738
|
\`\`\`
|
|
373
|
-
${requirementsContext.substring(0, 1000)}${requirementsContext.length > 1000 ?
|
|
739
|
+
${requirementsContext.substring(0, 1000)}${requirementsContext.length > 1000 ? "...\n[Requirements truncated - see requirements.md for full content]" : ""}
|
|
374
740
|
\`\`\`
|
|
375
741
|
|
|
376
742
|
## Design Context
|
|
377
743
|
\`\`\`
|
|
378
|
-
${designContext.substring(0, 1000)}${designContext.length > 1000 ?
|
|
744
|
+
${designContext.substring(0, 1000)}${designContext.length > 1000 ? "...\n[Design truncated - see design.md for full content]" : ""}
|
|
379
745
|
\`\`\`
|
|
380
746
|
|
|
381
747
|
## Current Project Information
|
|
@@ -386,504 +752,635 @@ ${designContext.substring(0, 1000)}${designContext.length > 1000 ? '...\n[Design
|
|
|
386
752
|
|
|
387
753
|
**Note**: This template will be replaced by AI-generated implementation tasks specific to your project requirements and design.`;
|
|
388
754
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
755
|
+
// Try to replace template with analysis-backed tasks
|
|
756
|
+
try {
|
|
757
|
+
const { mod } = await loadSpecGenerator();
|
|
758
|
+
tasksContent = await mod.generateTasksDocument(
|
|
759
|
+
currentPath,
|
|
760
|
+
featureName,
|
|
761
|
+
);
|
|
762
|
+
} catch (e) {
|
|
763
|
+
// Keep template; include debug info in file header already
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
await fs.writeFile(path.join(featurePath, "tasks.md"), tasksContent);
|
|
767
|
+
|
|
768
|
+
// Update spec.json
|
|
769
|
+
spec.phase = "tasks-generated";
|
|
770
|
+
spec.approvals.tasks.generated = true;
|
|
771
|
+
spec.ready_for_implementation = true;
|
|
772
|
+
spec.updated_at = await getCurrentTimestamp();
|
|
773
|
+
|
|
774
|
+
await fs.writeFile(specPath, JSON.stringify(spec, null, 2));
|
|
775
|
+
|
|
776
|
+
return {
|
|
777
|
+
content: [
|
|
778
|
+
{
|
|
779
|
+
type: "text",
|
|
780
|
+
text: `## Implementation Tasks Generated\n\nImplementation tasks document generated for feature: **${featureName}**\n\n**Generated**: .kiro/specs/${featureName}/tasks.md\n**Status**: Tasks phase completed\n**Ready for Implementation**: Yes\n\n**Next Step**: Review tasks, then use \`sdd-implement\` to begin implementation or \`sdd-status\` to check progress`,
|
|
781
|
+
},
|
|
782
|
+
],
|
|
783
|
+
};
|
|
784
|
+
} catch (error) {
|
|
785
|
+
return {
|
|
786
|
+
content: [
|
|
787
|
+
{
|
|
788
|
+
type: "text",
|
|
789
|
+
text: `Error generating tasks: ${error.message}`,
|
|
790
|
+
},
|
|
791
|
+
],
|
|
792
|
+
};
|
|
395
793
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
// Update spec.json
|
|
400
|
-
spec.phase = "tasks-generated";
|
|
401
|
-
spec.approvals.tasks.generated = true;
|
|
402
|
-
spec.ready_for_implementation = true;
|
|
403
|
-
spec.updated_at = await getCurrentTimestamp();
|
|
404
|
-
|
|
405
|
-
await fs.writeFile(specPath, JSON.stringify(spec, null, 2));
|
|
406
|
-
|
|
407
|
-
return {
|
|
408
|
-
content: [{
|
|
409
|
-
type: 'text',
|
|
410
|
-
text: `## Implementation Tasks Generated\n\nImplementation tasks document generated for feature: **${featureName}**\n\n**Generated**: .kiro/specs/${featureName}/tasks.md\n**Status**: Tasks phase completed\n**Ready for Implementation**: Yes\n\n**Next Step**: Review tasks, then use \`sdd-implement\` to begin implementation or \`sdd-status\` to check progress`
|
|
411
|
-
}]
|
|
412
|
-
};
|
|
413
|
-
} catch (error) {
|
|
414
|
-
return {
|
|
415
|
-
content: [{
|
|
416
|
-
type: 'text',
|
|
417
|
-
text: `Error generating tasks: ${error.message}`
|
|
418
|
-
}]
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
});
|
|
794
|
+
},
|
|
795
|
+
);
|
|
422
796
|
|
|
423
797
|
// 5. sdd-implement - Implementation guidelines
|
|
424
|
-
server.registerTool(
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
798
|
+
server.registerTool(
|
|
799
|
+
"sdd-implement",
|
|
800
|
+
{
|
|
801
|
+
title: "Implementation Guidelines",
|
|
802
|
+
description: "Implementation guidelines",
|
|
803
|
+
inputSchema: {
|
|
804
|
+
featureName: z.string().describe("Feature name from spec initialization"),
|
|
805
|
+
},
|
|
429
806
|
},
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
807
|
+
async ({ featureName }) => {
|
|
808
|
+
try {
|
|
809
|
+
const currentPath = process.cwd();
|
|
810
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
811
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
812
|
+
|
|
813
|
+
// Read spec
|
|
814
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
815
|
+
const spec = JSON.parse(specContent);
|
|
816
|
+
|
|
817
|
+
if (!spec.ready_for_implementation) {
|
|
818
|
+
return {
|
|
819
|
+
content: [
|
|
820
|
+
{
|
|
821
|
+
type: "text",
|
|
822
|
+
text: `Error: Project not ready for implementation. Complete requirements, design, and tasks phases first.`,
|
|
823
|
+
},
|
|
824
|
+
],
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
return {
|
|
829
|
+
content: [
|
|
830
|
+
{
|
|
831
|
+
type: "text",
|
|
832
|
+
text: `## Implementation Guidelines for ${featureName}\n\n**Project Status**: Ready for implementation\n**Current Phase**: ${spec.phase}\n\n**Implementation Instructions**:\n1. Work through tasks sequentially as defined in tasks.md\n2. Follow the technical design specifications in design.md\n3. Ensure all requirements from requirements.md are satisfied\n4. Use \`sdd-quality-check\` to validate code quality\n5. Mark tasks as completed in tasks.md as you progress\n\n**Key Principles**:\n- Follow established coding patterns and conventions\n- Implement comprehensive error handling\n- Add appropriate logging and monitoring\n- Write tests for each component\n- Validate against requirements at each step\n\n**Next Steps**:\n- Begin with Task 1: Set up MCP server foundation\n- Use the design document as your implementation guide\n- Run quality checks regularly during development`,
|
|
833
|
+
},
|
|
834
|
+
],
|
|
835
|
+
};
|
|
836
|
+
} catch (error) {
|
|
441
837
|
return {
|
|
442
|
-
content: [
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
838
|
+
content: [
|
|
839
|
+
{
|
|
840
|
+
type: "text",
|
|
841
|
+
text: `Error getting implementation guidelines: ${error.message}`,
|
|
842
|
+
},
|
|
843
|
+
],
|
|
446
844
|
};
|
|
447
845
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
content: [{
|
|
451
|
-
type: 'text',
|
|
452
|
-
text: `## Implementation Guidelines for ${featureName}\n\n**Project Status**: Ready for implementation\n**Current Phase**: ${spec.phase}\n\n**Implementation Instructions**:\n1. Work through tasks sequentially as defined in tasks.md\n2. Follow the technical design specifications in design.md\n3. Ensure all requirements from requirements.md are satisfied\n4. Use \`sdd-quality-check\` to validate code quality\n5. Mark tasks as completed in tasks.md as you progress\n\n**Key Principles**:\n- Follow established coding patterns and conventions\n- Implement comprehensive error handling\n- Add appropriate logging and monitoring\n- Write tests for each component\n- Validate against requirements at each step\n\n**Next Steps**:\n- Begin with Task 1: Set up MCP server foundation\n- Use the design document as your implementation guide\n- Run quality checks regularly during development`
|
|
453
|
-
}]
|
|
454
|
-
};
|
|
455
|
-
} catch (error) {
|
|
456
|
-
return {
|
|
457
|
-
content: [{
|
|
458
|
-
type: 'text',
|
|
459
|
-
text: `Error getting implementation guidelines: ${error.message}`
|
|
460
|
-
}]
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
});
|
|
846
|
+
},
|
|
847
|
+
);
|
|
464
848
|
|
|
465
849
|
// 6. sdd-status - Check workflow progress
|
|
466
|
-
server.registerTool(
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
850
|
+
server.registerTool(
|
|
851
|
+
"sdd-status",
|
|
852
|
+
{
|
|
853
|
+
title: "Check Workflow Progress",
|
|
854
|
+
description: "Check workflow progress",
|
|
855
|
+
inputSchema: {
|
|
856
|
+
featureName: z
|
|
857
|
+
.string()
|
|
858
|
+
.optional()
|
|
859
|
+
.describe("Feature name (optional - shows all if not provided)"),
|
|
860
|
+
},
|
|
471
861
|
},
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
text: 'SDD project status: No active project found. Use sdd-init to create a new project.'
|
|
484
|
-
}]
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const specsPath = path.join(kiroPath, 'specs');
|
|
489
|
-
|
|
490
|
-
if (featureName) {
|
|
491
|
-
// Show status for specific feature
|
|
492
|
-
const featurePath = path.join(specsPath, featureName);
|
|
493
|
-
const specPath = path.join(featurePath, 'spec.json');
|
|
494
|
-
|
|
495
|
-
const specExists = await fs.access(specPath).then(() => true).catch(() => false);
|
|
496
|
-
if (!specExists) {
|
|
862
|
+
async ({ featureName }) => {
|
|
863
|
+
try {
|
|
864
|
+
const currentPath = process.cwd();
|
|
865
|
+
const kiroPath = path.join(currentPath, ".kiro");
|
|
866
|
+
|
|
867
|
+
// Check if .kiro directory exists
|
|
868
|
+
const kiroExists = await fs
|
|
869
|
+
.access(kiroPath)
|
|
870
|
+
.then(() => true)
|
|
871
|
+
.catch(() => false);
|
|
872
|
+
if (!kiroExists) {
|
|
497
873
|
return {
|
|
498
|
-
content: [
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
874
|
+
content: [
|
|
875
|
+
{
|
|
876
|
+
type: "text",
|
|
877
|
+
text: "SDD project status: No active project found. Use sdd-init to create a new project.",
|
|
878
|
+
},
|
|
879
|
+
],
|
|
502
880
|
};
|
|
503
881
|
}
|
|
504
|
-
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
882
|
+
|
|
883
|
+
const specsPath = path.join(kiroPath, "specs");
|
|
884
|
+
|
|
885
|
+
if (featureName) {
|
|
886
|
+
// Show status for specific feature
|
|
887
|
+
const featurePath = path.join(specsPath, featureName);
|
|
888
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
889
|
+
|
|
890
|
+
const specExists = await fs
|
|
891
|
+
.access(specPath)
|
|
892
|
+
.then(() => true)
|
|
893
|
+
.catch(() => false);
|
|
894
|
+
if (!specExists) {
|
|
895
|
+
return {
|
|
896
|
+
content: [
|
|
897
|
+
{
|
|
898
|
+
type: "text",
|
|
899
|
+
text: `Feature "${featureName}" not found. Use sdd-init to create it.`,
|
|
900
|
+
},
|
|
901
|
+
],
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
906
|
+
const spec = JSON.parse(specContent);
|
|
907
|
+
|
|
908
|
+
let status = `## SDD Project Status: ${spec.feature_name}\n\n`;
|
|
909
|
+
status += `**Current Phase**: ${spec.phase}\n`;
|
|
910
|
+
status += `**Language**: ${spec.language}\n`;
|
|
911
|
+
status += `**Created**: ${spec.created_at}\n`;
|
|
912
|
+
status += `**Updated**: ${spec.updated_at}\n\n`;
|
|
913
|
+
|
|
914
|
+
status += `**Phase Progress**:\n`;
|
|
915
|
+
status += `- Requirements: ${spec.approvals.requirements.generated ? "✅ Generated" : "❌ Not Generated"}${spec.approvals.requirements.approved ? ", ✅ Approved" : ", ❌ Not Approved"}\n`;
|
|
916
|
+
status += `- Design: ${spec.approvals.design.generated ? "✅ Generated" : "❌ Not Generated"}${spec.approvals.design.approved ? ", ✅ Approved" : ", ❌ Not Approved"}\n`;
|
|
917
|
+
status += `- Tasks: ${spec.approvals.tasks.generated ? "✅ Generated" : "❌ Not Generated"}${spec.approvals.tasks.approved ? ", ✅ Approved" : ", ❌ Not Approved"}\n\n`;
|
|
918
|
+
|
|
919
|
+
status += `**Ready for Implementation**: ${spec.ready_for_implementation ? "✅ Yes" : "❌ No"}\n\n`;
|
|
920
|
+
|
|
921
|
+
// Suggest next steps
|
|
922
|
+
if (!spec.approvals.requirements.generated) {
|
|
923
|
+
status += `**Next Step**: Run \`sdd-requirements ${featureName}\``;
|
|
924
|
+
} else if (!spec.approvals.design.generated) {
|
|
925
|
+
status += `**Next Step**: Run \`sdd-design ${featureName}\``;
|
|
926
|
+
} else if (!spec.approvals.tasks.generated) {
|
|
927
|
+
status += `**Next Step**: Run \`sdd-tasks ${featureName}\``;
|
|
928
|
+
} else {
|
|
929
|
+
status += `**Next Step**: Run \`sdd-implement ${featureName}\` to begin implementation`;
|
|
930
|
+
}
|
|
931
|
+
|
|
543
932
|
return {
|
|
544
|
-
content: [
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
933
|
+
content: [
|
|
934
|
+
{
|
|
935
|
+
type: "text",
|
|
936
|
+
text: status,
|
|
937
|
+
},
|
|
938
|
+
],
|
|
548
939
|
};
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
940
|
+
} else {
|
|
941
|
+
// Show all features
|
|
942
|
+
const features = await fs.readdir(specsPath).catch(() => []);
|
|
943
|
+
|
|
944
|
+
if (features.length === 0) {
|
|
945
|
+
return {
|
|
946
|
+
content: [
|
|
947
|
+
{
|
|
948
|
+
type: "text",
|
|
949
|
+
text: "No SDD features found. Use sdd-init to create a new project.",
|
|
950
|
+
},
|
|
951
|
+
],
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
let status = `## SDD Project Status - All Features\n\n`;
|
|
956
|
+
|
|
957
|
+
for (const feature of features) {
|
|
958
|
+
const specPath = path.join(specsPath, feature, "spec.json");
|
|
959
|
+
const specExists = await fs
|
|
960
|
+
.access(specPath)
|
|
961
|
+
.then(() => true)
|
|
962
|
+
.catch(() => false);
|
|
963
|
+
|
|
964
|
+
if (specExists) {
|
|
965
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
966
|
+
const spec = JSON.parse(specContent);
|
|
967
|
+
|
|
968
|
+
status += `**${spec.feature_name}**:\n`;
|
|
969
|
+
status += `- Phase: ${spec.phase}\n`;
|
|
970
|
+
status += `- Requirements: ${spec.approvals.requirements.generated ? "✅" : "❌"}\n`;
|
|
971
|
+
status += `- Design: ${spec.approvals.design.generated ? "✅" : "❌"}\n`;
|
|
972
|
+
status += `- Tasks: ${spec.approvals.tasks.generated ? "✅" : "❌"}\n`;
|
|
973
|
+
status += `- Ready: ${spec.ready_for_implementation ? "✅" : "❌"}\n\n`;
|
|
974
|
+
}
|
|
567
975
|
}
|
|
976
|
+
|
|
977
|
+
return {
|
|
978
|
+
content: [
|
|
979
|
+
{
|
|
980
|
+
type: "text",
|
|
981
|
+
text: status,
|
|
982
|
+
},
|
|
983
|
+
],
|
|
984
|
+
};
|
|
568
985
|
}
|
|
569
|
-
|
|
986
|
+
} catch (error) {
|
|
570
987
|
return {
|
|
571
|
-
content: [
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
988
|
+
content: [
|
|
989
|
+
{
|
|
990
|
+
type: "text",
|
|
991
|
+
text: `Error checking status: ${error.message}`,
|
|
992
|
+
},
|
|
993
|
+
],
|
|
575
994
|
};
|
|
576
995
|
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
content: [{
|
|
580
|
-
type: 'text',
|
|
581
|
-
text: `Error checking status: ${error.message}`
|
|
582
|
-
}]
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
});
|
|
996
|
+
},
|
|
997
|
+
);
|
|
586
998
|
|
|
587
999
|
// 7. sdd-approve - Approve workflow phases
|
|
588
|
-
server.registerTool(
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
1000
|
+
server.registerTool(
|
|
1001
|
+
"sdd-approve",
|
|
1002
|
+
{
|
|
1003
|
+
title: "Approve Workflow Phases",
|
|
1004
|
+
description: "Approve workflow phases",
|
|
1005
|
+
inputSchema: {
|
|
1006
|
+
featureName: z.string().describe("Feature name from spec initialization"),
|
|
1007
|
+
phase: z
|
|
1008
|
+
.enum(["requirements", "design", "tasks"])
|
|
1009
|
+
.describe("Phase to approve"),
|
|
1010
|
+
},
|
|
594
1011
|
},
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
1012
|
+
async ({ featureName, phase }) => {
|
|
1013
|
+
try {
|
|
1014
|
+
const currentPath = process.cwd();
|
|
1015
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
1016
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
1017
|
+
|
|
1018
|
+
// Read spec
|
|
1019
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
1020
|
+
const spec = JSON.parse(specContent);
|
|
1021
|
+
|
|
1022
|
+
if (!spec.approvals[phase].generated) {
|
|
1023
|
+
return {
|
|
1024
|
+
content: [
|
|
1025
|
+
{
|
|
1026
|
+
type: "text",
|
|
1027
|
+
text: `Error: ${phase} must be generated before approval. Run sdd-${phase} ${featureName} first.`,
|
|
1028
|
+
},
|
|
1029
|
+
],
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// Approve the phase
|
|
1034
|
+
spec.approvals[phase].approved = true;
|
|
1035
|
+
spec.updated_at = await getCurrentTimestamp();
|
|
1036
|
+
|
|
1037
|
+
await fs.writeFile(specPath, JSON.stringify(spec, null, 2));
|
|
1038
|
+
|
|
606
1039
|
return {
|
|
607
|
-
content: [
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
1040
|
+
content: [
|
|
1041
|
+
{
|
|
1042
|
+
type: "text",
|
|
1043
|
+
text: `## Phase Approved\n\n**Feature**: ${featureName}\n**Phase**: ${phase}\n**Status**: ✅ Approved\n\nPhase has been marked as approved and workflow can proceed to the next phase.`,
|
|
1044
|
+
},
|
|
1045
|
+
],
|
|
1046
|
+
};
|
|
1047
|
+
} catch (error) {
|
|
1048
|
+
return {
|
|
1049
|
+
content: [
|
|
1050
|
+
{
|
|
1051
|
+
type: "text",
|
|
1052
|
+
text: `Error approving phase: ${error.message}`,
|
|
1053
|
+
},
|
|
1054
|
+
],
|
|
611
1055
|
};
|
|
612
1056
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
spec.approvals[phase].approved = true;
|
|
616
|
-
spec.updated_at = await getCurrentTimestamp();
|
|
617
|
-
|
|
618
|
-
await fs.writeFile(specPath, JSON.stringify(spec, null, 2));
|
|
619
|
-
|
|
620
|
-
return {
|
|
621
|
-
content: [{
|
|
622
|
-
type: 'text',
|
|
623
|
-
text: `## Phase Approved\n\n**Feature**: ${featureName}\n**Phase**: ${phase}\n**Status**: ✅ Approved\n\nPhase has been marked as approved and workflow can proceed to the next phase.`
|
|
624
|
-
}]
|
|
625
|
-
};
|
|
626
|
-
} catch (error) {
|
|
627
|
-
return {
|
|
628
|
-
content: [{
|
|
629
|
-
type: 'text',
|
|
630
|
-
text: `Error approving phase: ${error.message}`
|
|
631
|
-
}]
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
});
|
|
1057
|
+
},
|
|
1058
|
+
);
|
|
635
1059
|
|
|
636
1060
|
// 8. sdd-quality-check - Code quality analysis
|
|
637
|
-
server.registerTool(
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
1061
|
+
server.registerTool(
|
|
1062
|
+
"sdd-quality-check",
|
|
1063
|
+
{
|
|
1064
|
+
title: "Code Quality Analysis",
|
|
1065
|
+
description: "Code quality analysis",
|
|
1066
|
+
inputSchema: {
|
|
1067
|
+
code: z.string().describe("Code to analyze"),
|
|
1068
|
+
language: z
|
|
1069
|
+
.string()
|
|
1070
|
+
.optional()
|
|
1071
|
+
.describe("Programming language (default: javascript)"),
|
|
1072
|
+
},
|
|
643
1073
|
},
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
if (code.includes('var ')) {
|
|
655
|
-
issues.push('L1: Use let/const instead of var');
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
// Layer 2: Code Style and Conventions
|
|
659
|
-
if (!/^[a-z]/.test(code.split('function ')[1]?.split('(')[0] || '')) {
|
|
660
|
-
if (code.includes('function ')) {
|
|
661
|
-
issues.push('L2: Function names should start with lowercase');
|
|
1074
|
+
async ({ code, language = "javascript" }) => {
|
|
1075
|
+
try {
|
|
1076
|
+
// Simple quality analysis (Linus-style 5-layer approach)
|
|
1077
|
+
const lines = code.split("\n");
|
|
1078
|
+
const issues = [];
|
|
1079
|
+
|
|
1080
|
+
// Layer 1: Syntax and Basic Structure
|
|
1081
|
+
if (code.includes("console.log")) {
|
|
1082
|
+
issues.push("L1: Remove debug console.log statements");
|
|
662
1083
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1084
|
+
if (code.includes("var ")) {
|
|
1085
|
+
issues.push("L1: Use let/const instead of var");
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// Layer 2: Code Style and Conventions
|
|
1089
|
+
if (!/^[a-z]/.test(code.split("function ")[1]?.split("(")[0] || "")) {
|
|
1090
|
+
if (code.includes("function ")) {
|
|
1091
|
+
issues.push("L2: Function names should start with lowercase");
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// Layer 3: Logic and Algorithm
|
|
1096
|
+
if (code.includes("for") && !code.includes("const")) {
|
|
1097
|
+
issues.push(
|
|
1098
|
+
"L3: Consider using const in for loops for immutable iteration variables",
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Layer 4: Architecture and Design
|
|
1103
|
+
if (lines.length > 50) {
|
|
1104
|
+
issues.push(
|
|
1105
|
+
"L4: Function/module is too long, consider breaking into smaller parts",
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// Layer 5: Business Logic and Requirements
|
|
1110
|
+
if (!code.includes("error") && code.includes("try")) {
|
|
1111
|
+
issues.push("L5: Missing proper error handling in try block");
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
const qualityScore = Math.max(0, 100 - issues.length * 15);
|
|
1115
|
+
|
|
1116
|
+
let report = `## Linus-Style Code Quality Analysis\n\n`;
|
|
1117
|
+
report += `**Language**: ${language}\n`;
|
|
1118
|
+
report += `**Lines of Code**: ${lines.length}\n`;
|
|
1119
|
+
report += `**Quality Score**: ${qualityScore}/100\n\n`;
|
|
1120
|
+
|
|
1121
|
+
if (issues.length === 0) {
|
|
1122
|
+
report += `**Status**: ✅ Code quality is excellent\n\n`;
|
|
1123
|
+
report += `**Analysis**: No significant issues found. Code follows good practices.`;
|
|
1124
|
+
} else {
|
|
1125
|
+
report += `**Issues Found**: ${issues.length}\n\n`;
|
|
1126
|
+
report += `**Quality Issues**:\n`;
|
|
1127
|
+
for (const issue of issues) {
|
|
1128
|
+
report += `- ${issue}\n`;
|
|
1129
|
+
}
|
|
1130
|
+
report += `\n**Recommendation**: Address the identified issues to improve code quality.`;
|
|
695
1131
|
}
|
|
696
|
-
|
|
1132
|
+
|
|
1133
|
+
return {
|
|
1134
|
+
content: [
|
|
1135
|
+
{
|
|
1136
|
+
type: "text",
|
|
1137
|
+
text: report,
|
|
1138
|
+
},
|
|
1139
|
+
],
|
|
1140
|
+
};
|
|
1141
|
+
} catch (error) {
|
|
1142
|
+
return {
|
|
1143
|
+
content: [
|
|
1144
|
+
{
|
|
1145
|
+
type: "text",
|
|
1146
|
+
text: `Error analyzing code quality: ${error.message}`,
|
|
1147
|
+
},
|
|
1148
|
+
],
|
|
1149
|
+
};
|
|
697
1150
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
content: [{
|
|
701
|
-
type: 'text',
|
|
702
|
-
text: report
|
|
703
|
-
}]
|
|
704
|
-
};
|
|
705
|
-
} catch (error) {
|
|
706
|
-
return {
|
|
707
|
-
content: [{
|
|
708
|
-
type: 'text',
|
|
709
|
-
text: `Error analyzing code quality: ${error.message}`
|
|
710
|
-
}]
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
});
|
|
1151
|
+
},
|
|
1152
|
+
);
|
|
714
1153
|
|
|
715
1154
|
// 9. sdd-context-load - Load project context
|
|
716
|
-
server.registerTool(
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
1155
|
+
server.registerTool(
|
|
1156
|
+
"sdd-context-load",
|
|
1157
|
+
{
|
|
1158
|
+
title: "Load Project Context",
|
|
1159
|
+
description: "Load project context",
|
|
1160
|
+
inputSchema: {
|
|
1161
|
+
featureName: z.string().describe("Feature name to load context for"),
|
|
1162
|
+
},
|
|
721
1163
|
},
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1164
|
+
async ({ featureName }) => {
|
|
1165
|
+
try {
|
|
1166
|
+
const currentPath = process.cwd();
|
|
1167
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
1168
|
+
|
|
1169
|
+
// Load all context files
|
|
1170
|
+
const files = ["spec.json", "requirements.md", "design.md", "tasks.md"];
|
|
1171
|
+
const context = {};
|
|
1172
|
+
|
|
1173
|
+
for (const file of files) {
|
|
1174
|
+
const filePath = path.join(featurePath, file);
|
|
1175
|
+
const fileExists = await fs
|
|
1176
|
+
.access(filePath)
|
|
1177
|
+
.then(() => true)
|
|
1178
|
+
.catch(() => false);
|
|
1179
|
+
|
|
1180
|
+
if (fileExists) {
|
|
1181
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
1182
|
+
context[file] = file.endsWith(".json")
|
|
1183
|
+
? JSON.parse(content)
|
|
1184
|
+
: content;
|
|
1185
|
+
}
|
|
738
1186
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1187
|
+
|
|
1188
|
+
let contextReport = `## Project Context Loaded: ${featureName}\n\n`;
|
|
1189
|
+
|
|
1190
|
+
if (context["spec.json"]) {
|
|
1191
|
+
const spec = context["spec.json"];
|
|
1192
|
+
contextReport += `**Project Metadata**:\n`;
|
|
1193
|
+
contextReport += `- Feature: ${spec.feature_name}\n`;
|
|
1194
|
+
contextReport += `- Phase: ${spec.phase}\n`;
|
|
1195
|
+
contextReport += `- Language: ${spec.language}\n`;
|
|
1196
|
+
contextReport += `- Ready for Implementation: ${spec.ready_for_implementation ? "Yes" : "No"}\n\n`;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
contextReport += `**Available Documents**:\n`;
|
|
1200
|
+
for (const [file, content] of Object.entries(context)) {
|
|
1201
|
+
if (file !== "spec.json") {
|
|
1202
|
+
const preview =
|
|
1203
|
+
typeof content === "string"
|
|
1204
|
+
? content.substring(0, 100) + "..."
|
|
1205
|
+
: "JSON data";
|
|
1206
|
+
contextReport += `- **${file}**: ${preview}\n`;
|
|
1207
|
+
}
|
|
757
1208
|
}
|
|
1209
|
+
|
|
1210
|
+
contextReport += `\n**Context Status**: Project memory restored successfully\n`;
|
|
1211
|
+
contextReport += `**Total Files Loaded**: ${Object.keys(context).length}`;
|
|
1212
|
+
|
|
1213
|
+
return {
|
|
1214
|
+
content: [
|
|
1215
|
+
{
|
|
1216
|
+
type: "text",
|
|
1217
|
+
text: contextReport,
|
|
1218
|
+
},
|
|
1219
|
+
],
|
|
1220
|
+
};
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
return {
|
|
1223
|
+
content: [
|
|
1224
|
+
{
|
|
1225
|
+
type: "text",
|
|
1226
|
+
text: `Error loading project context: ${error.message}`,
|
|
1227
|
+
},
|
|
1228
|
+
],
|
|
1229
|
+
};
|
|
758
1230
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
contextReport += `**Total Files Loaded**: ${Object.keys(context).length}`;
|
|
762
|
-
|
|
763
|
-
return {
|
|
764
|
-
content: [{
|
|
765
|
-
type: 'text',
|
|
766
|
-
text: contextReport
|
|
767
|
-
}]
|
|
768
|
-
};
|
|
769
|
-
} catch (error) {
|
|
770
|
-
return {
|
|
771
|
-
content: [{
|
|
772
|
-
type: 'text',
|
|
773
|
-
text: `Error loading project context: ${error.message}`
|
|
774
|
-
}]
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
});
|
|
1231
|
+
},
|
|
1232
|
+
);
|
|
778
1233
|
|
|
779
1234
|
// 10. sdd-template-render - Render templates
|
|
780
|
-
server.registerTool(
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1235
|
+
server.registerTool(
|
|
1236
|
+
"sdd-template-render",
|
|
1237
|
+
{
|
|
1238
|
+
title: "Render Templates",
|
|
1239
|
+
description: "Render templates",
|
|
1240
|
+
inputSchema: {
|
|
1241
|
+
templateType: z
|
|
1242
|
+
.enum(["requirements", "design", "tasks", "custom"])
|
|
1243
|
+
.describe("Type of template to render"),
|
|
1244
|
+
featureName: z.string().describe("Feature name for template context"),
|
|
1245
|
+
customTemplate: z
|
|
1246
|
+
.string()
|
|
1247
|
+
.optional()
|
|
1248
|
+
.describe("Custom template content (if templateType is custom)"),
|
|
1249
|
+
},
|
|
787
1250
|
},
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
spec =
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1251
|
+
async ({ templateType, featureName, customTemplate }) => {
|
|
1252
|
+
try {
|
|
1253
|
+
const currentPath = process.cwd();
|
|
1254
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
1255
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
1256
|
+
|
|
1257
|
+
// Load spec for context
|
|
1258
|
+
const specExists = await fs
|
|
1259
|
+
.access(specPath)
|
|
1260
|
+
.then(() => true)
|
|
1261
|
+
.catch(() => false);
|
|
1262
|
+
let spec = {};
|
|
1263
|
+
if (specExists) {
|
|
1264
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
1265
|
+
spec = JSON.parse(specContent);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
let renderedContent = "";
|
|
1269
|
+
|
|
1270
|
+
switch (templateType) {
|
|
1271
|
+
case "requirements":
|
|
1272
|
+
renderedContent = `# Requirements Template for ${featureName}\n\n## Project Context\n- Feature: ${spec.feature_name || featureName}\n- Language: ${spec.language || "en"}\n\n## Requirements Sections\n1. Functional Requirements\n2. Non-Functional Requirements\n3. Business Rules\n4. Acceptance Criteria (EARS format)`;
|
|
1273
|
+
break;
|
|
1274
|
+
|
|
1275
|
+
case "design":
|
|
1276
|
+
renderedContent = `# Design Template for ${featureName}\n\n## Architecture Overview\n- System Architecture\n- Component Design\n- Data Models\n- Interface Specifications\n\n## Technology Decisions\n- Stack Selection\n- Framework Choices\n- Integration Patterns`;
|
|
1277
|
+
break;
|
|
1278
|
+
|
|
1279
|
+
case "tasks":
|
|
1280
|
+
renderedContent = `# Tasks Template for ${featureName}\n\n## Implementation Tasks\n\n- [ ] 1. Foundation Setup\n - Project initialization\n - Dependencies setup\n - Basic structure\n\n- [ ] 2. Core Implementation\n - Main functionality\n - Business logic\n - Data handling\n\n- [ ] 3. Integration & Testing\n - System integration\n - Testing implementation\n - Quality validation`;
|
|
1281
|
+
break;
|
|
1282
|
+
|
|
1283
|
+
case "custom":
|
|
1284
|
+
if (!customTemplate) {
|
|
1285
|
+
throw new Error(
|
|
1286
|
+
"Custom template content is required for custom template type",
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
// Simple template variable replacement
|
|
1290
|
+
renderedContent = customTemplate
|
|
1291
|
+
.replace(/\{\{featureName\}\}/g, featureName)
|
|
1292
|
+
.replace(/\{\{language\}\}/g, spec.language || "en")
|
|
1293
|
+
.replace(/\{\{phase\}\}/g, spec.phase || "initialized")
|
|
1294
|
+
.replace(/\{\{timestamp\}\}/g, new Date().toISOString());
|
|
1295
|
+
break;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
return {
|
|
1299
|
+
content: [
|
|
1300
|
+
{
|
|
1301
|
+
type: "text",
|
|
1302
|
+
text: `## Template Rendered: ${templateType}\n\n**Feature**: ${featureName}\n**Template Type**: ${templateType}\n\n**Rendered Content**:\n\`\`\`markdown\n${renderedContent}\n\`\`\`\n\n**Usage**: Copy the rendered content to create your ${templateType} document`,
|
|
1303
|
+
},
|
|
1304
|
+
],
|
|
1305
|
+
};
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
return {
|
|
1308
|
+
content: [
|
|
1309
|
+
{
|
|
1310
|
+
type: "text",
|
|
1311
|
+
text: `Error rendering template: ${error.message}`,
|
|
1312
|
+
},
|
|
1313
|
+
],
|
|
1314
|
+
};
|
|
828
1315
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
content: [{
|
|
832
|
-
type: 'text',
|
|
833
|
-
text: `## Template Rendered: ${templateType}\n\n**Feature**: ${featureName}\n**Template Type**: ${templateType}\n\n**Rendered Content**:\n\`\`\`markdown\n${renderedContent}\n\`\`\`\n\n**Usage**: Copy the rendered content to create your ${templateType} document`
|
|
834
|
-
}]
|
|
835
|
-
};
|
|
836
|
-
} catch (error) {
|
|
837
|
-
return {
|
|
838
|
-
content: [{
|
|
839
|
-
type: 'text',
|
|
840
|
-
text: `Error rendering template: ${error.message}`
|
|
841
|
-
}]
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
});
|
|
1316
|
+
},
|
|
1317
|
+
);
|
|
845
1318
|
|
|
846
1319
|
// 11. sdd-steering - Create/update steering documents
|
|
847
|
-
server.registerTool(
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
1320
|
+
server.registerTool(
|
|
1321
|
+
"sdd-steering",
|
|
1322
|
+
{
|
|
1323
|
+
title: "Create/Update Steering Documents",
|
|
1324
|
+
description: "Create or update steering documents",
|
|
1325
|
+
inputSchema: {
|
|
1326
|
+
updateMode: z
|
|
1327
|
+
.enum(["create", "update"])
|
|
1328
|
+
.optional()
|
|
1329
|
+
.describe(
|
|
1330
|
+
"Whether to create new or update existing documents (auto-detected if not specified)",
|
|
1331
|
+
),
|
|
1332
|
+
},
|
|
852
1333
|
},
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1334
|
+
async ({ updateMode }) => {
|
|
1335
|
+
try {
|
|
1336
|
+
const currentPath = process.cwd();
|
|
1337
|
+
const steeringPath = path.join(currentPath, ".kiro", "steering");
|
|
1338
|
+
|
|
1339
|
+
// Create steering directory if it doesn't exist
|
|
1340
|
+
await fs.mkdir(steeringPath, { recursive: true });
|
|
1341
|
+
|
|
1342
|
+
// Analyze existing files
|
|
1343
|
+
const productExists = await fs
|
|
1344
|
+
.access(path.join(steeringPath, "product.md"))
|
|
1345
|
+
.then(() => true)
|
|
1346
|
+
.catch(() => false);
|
|
1347
|
+
const techExists = await fs
|
|
1348
|
+
.access(path.join(steeringPath, "tech.md"))
|
|
1349
|
+
.then(() => true)
|
|
1350
|
+
.catch(() => false);
|
|
1351
|
+
const structureExists = await fs
|
|
1352
|
+
.access(path.join(steeringPath, "structure.md"))
|
|
1353
|
+
.then(() => true)
|
|
1354
|
+
.catch(() => false);
|
|
1355
|
+
|
|
1356
|
+
// Auto-detect mode if not specified
|
|
1357
|
+
if (!updateMode) {
|
|
1358
|
+
updateMode =
|
|
1359
|
+
productExists || techExists || structureExists ? "update" : "create";
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// Generate actual analyzed content using documentGenerator functions
|
|
1363
|
+
const analysis = await analyzeProject(currentPath);
|
|
1364
|
+
const productContent = generateProductDocument(analysis);
|
|
1365
|
+
const techContent = generateTechDocument(analysis);
|
|
1366
|
+
const structureContent = generateStructureDocument(analysis);
|
|
1367
|
+
|
|
1368
|
+
// Write the analyzed steering documents
|
|
1369
|
+
await fs.writeFile(path.join(steeringPath, "product.md"), productContent);
|
|
1370
|
+
await fs.writeFile(path.join(steeringPath, "tech.md"), techContent);
|
|
1371
|
+
await fs.writeFile(
|
|
1372
|
+
path.join(steeringPath, "structure.md"),
|
|
1373
|
+
structureContent,
|
|
1374
|
+
);
|
|
1375
|
+
|
|
1376
|
+
// Ensure static steering docs exist (full content)
|
|
1377
|
+
const linusPath = path.join(steeringPath, "linus-review.md");
|
|
1378
|
+
const linusExists = await fs
|
|
1379
|
+
.access(linusPath)
|
|
1380
|
+
.then(() => true)
|
|
1381
|
+
.catch(() => false);
|
|
1382
|
+
if (!linusExists) {
|
|
1383
|
+
const fullLinusContent = `# Linus Torvalds Code Review Steering Document
|
|
887
1384
|
|
|
888
1385
|
## Role Definition
|
|
889
1386
|
|
|
@@ -1036,12 +1533,15 @@ This steering document is applied when:
|
|
|
1036
1533
|
- Code review: Apply taste scoring and improvement recommendations
|
|
1037
1534
|
|
|
1038
1535
|
Remember: "Good taste" comes from experience. Question everything. Simplify ruthlessly. Never break userspace.`;
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1536
|
+
await fs.writeFile(linusPath, fullLinusContent);
|
|
1537
|
+
}
|
|
1538
|
+
const commitPath = path.join(steeringPath, "commit.md");
|
|
1539
|
+
const commitExists = await fs
|
|
1540
|
+
.access(commitPath)
|
|
1541
|
+
.then(() => true)
|
|
1542
|
+
.catch(() => false);
|
|
1543
|
+
if (!commitExists) {
|
|
1544
|
+
const fullCommitContent = `# Commit Message Guidelines
|
|
1045
1545
|
|
|
1046
1546
|
Commit messages should follow a consistent format to improve readability and provide clear context about changes. Each commit message should start with a type prefix that indicates the nature of the change.
|
|
1047
1547
|
|
|
@@ -1100,26 +1600,35 @@ refactor(module): simplify EKS node group module
|
|
|
1100
1600
|
6. Reference issues and pull requests in the footer
|
|
1101
1601
|
|
|
1102
1602
|
These guidelines help maintain a clean and useful git history that makes it easier to track changes and understand the project's evolution.`;
|
|
1103
|
-
|
|
1104
|
-
|
|
1603
|
+
await fs.writeFile(commitPath, fullCommitContent);
|
|
1604
|
+
}
|
|
1105
1605
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if (
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
.
|
|
1117
|
-
.
|
|
1118
|
-
.
|
|
1119
|
-
|
|
1120
|
-
.
|
|
1121
|
-
|
|
1122
|
-
|
|
1606
|
+
// Ensure AGENTS.md exists in steering directory (create from CLAUDE.md if available)
|
|
1607
|
+
const agentsPath = path.join(steeringPath, "AGENTS.md");
|
|
1608
|
+
const claudePath = path.join(currentPath, "CLAUDE.md");
|
|
1609
|
+
const agentsExists = await fs
|
|
1610
|
+
.access(agentsPath)
|
|
1611
|
+
.then(() => true)
|
|
1612
|
+
.catch(() => false);
|
|
1613
|
+
if (!agentsExists) {
|
|
1614
|
+
let agentsContent = "";
|
|
1615
|
+
const claudeExists = await fs
|
|
1616
|
+
.access(claudePath)
|
|
1617
|
+
.then(() => true)
|
|
1618
|
+
.catch(() => false);
|
|
1619
|
+
if (claudeExists) {
|
|
1620
|
+
const claude = await fs.readFile(claudePath, "utf8");
|
|
1621
|
+
agentsContent = claude
|
|
1622
|
+
.replace(
|
|
1623
|
+
/# Claude Code Spec-Driven Development/g,
|
|
1624
|
+
"# AI Agent Spec-Driven Development",
|
|
1625
|
+
)
|
|
1626
|
+
.replace(/Claude Code/g, "AI Agent")
|
|
1627
|
+
.replace(/claude code/g, "ai agent")
|
|
1628
|
+
.replace(/\.claude\//g, ".ai agent/")
|
|
1629
|
+
.replace(/\/claude/g, "/agent");
|
|
1630
|
+
} else {
|
|
1631
|
+
agentsContent = `# AI Agent Spec-Driven Development
|
|
1123
1632
|
|
|
1124
1633
|
Kiro-style Spec Driven Development implementation using ai agent slash commands, hooks and agents.
|
|
1125
1634
|
|
|
@@ -1195,15 +1704,18 @@ Managed by \`/kiro:steering\` command. Updates here reflect command changes.
|
|
|
1195
1704
|
- **Always**: Loaded in every interaction (default)
|
|
1196
1705
|
- **Conditional**: Loaded for specific file patterns (e.g., "*.test.js")
|
|
1197
1706
|
- **Manual**: Reference with \`@filename.md\` syntax`;
|
|
1707
|
+
}
|
|
1708
|
+
await fs.writeFile(agentsPath, agentsContent);
|
|
1198
1709
|
}
|
|
1199
|
-
await fs.writeFile(agentsPath, agentsContent);
|
|
1200
|
-
}
|
|
1201
1710
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1711
|
+
// Ensure security-check.md exists (static)
|
|
1712
|
+
const securityPath = path.join(steeringPath, "security-check.md");
|
|
1713
|
+
const securityExists = await fs
|
|
1714
|
+
.access(securityPath)
|
|
1715
|
+
.then(() => true)
|
|
1716
|
+
.catch(() => false);
|
|
1717
|
+
if (!securityExists) {
|
|
1718
|
+
const securityContent = `# Security Check (OWASP Top 10 Aligned)
|
|
1207
1719
|
|
|
1208
1720
|
Use this checklist during code generation and review. Avoid OWASP Top 10 issues by design.
|
|
1209
1721
|
|
|
@@ -1253,13 +1765,16 @@ Use this checklist during code generation and review. Avoid OWASP Top 10 issues
|
|
|
1253
1765
|
- Use content security best practices for templates/HTML
|
|
1254
1766
|
- Add security tests where feasible (authz, input validation)
|
|
1255
1767
|
`;
|
|
1256
|
-
|
|
1257
|
-
|
|
1768
|
+
await fs.writeFile(securityPath, securityContent);
|
|
1769
|
+
}
|
|
1258
1770
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1771
|
+
const tddPath = path.join(steeringPath, "tdd-guideline.md");
|
|
1772
|
+
const tddExists = await fs
|
|
1773
|
+
.access(tddPath)
|
|
1774
|
+
.then(() => true)
|
|
1775
|
+
.catch(() => false);
|
|
1776
|
+
if (!tddExists) {
|
|
1777
|
+
const tddContent = `# Test-Driven Development (TDD) Guideline
|
|
1263
1778
|
|
|
1264
1779
|
## Purpose
|
|
1265
1780
|
This steering document defines TDD practices and workflow to ensure test-first development throughout the project lifecycle.
|
|
@@ -1503,13 +2018,16 @@ npm test path/to/test.test.ts
|
|
|
1503
2018
|
## Enforcement
|
|
1504
2019
|
This document is **always** active and applies to all development phases. Every code change should follow TDD principles as defined here.
|
|
1505
2020
|
`;
|
|
1506
|
-
|
|
1507
|
-
|
|
2021
|
+
await fs.writeFile(tddPath, tddContent);
|
|
2022
|
+
}
|
|
1508
2023
|
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
2024
|
+
const principlesPath = path.join(steeringPath, "principles.md");
|
|
2025
|
+
const principlesExists = await fs
|
|
2026
|
+
.access(principlesPath)
|
|
2027
|
+
.then(() => true)
|
|
2028
|
+
.catch(() => false);
|
|
2029
|
+
if (!principlesExists) {
|
|
2030
|
+
const principlesContent = `# Core Coding Principles and Patterns
|
|
1513
2031
|
|
|
1514
2032
|
Follow SOLID, DRY, KISS, YAGNI, Separation of Concerns, and Modularity in all code.
|
|
1515
2033
|
|
|
@@ -1547,15 +2065,16 @@ High cohesion, low coupling. Encapsulate implementation details.
|
|
|
1547
2065
|
|
|
1548
2066
|
Refer to full principles.md for detailed examples and language-specific guidance.
|
|
1549
2067
|
`;
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
2068
|
+
await fs.writeFile(principlesPath, principlesContent);
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
const mode = updateMode === "update" ? "Updated" : "Created";
|
|
2072
|
+
|
|
2073
|
+
return {
|
|
2074
|
+
content: [
|
|
2075
|
+
{
|
|
2076
|
+
type: "text",
|
|
2077
|
+
text: `## Steering Documents ${mode}
|
|
1559
2078
|
|
|
1560
2079
|
**Project Path**: ${currentPath}
|
|
1561
2080
|
**Mode**: ${updateMode}
|
|
@@ -1584,66 +2103,88 @@ The steering documents now contain analysis instructions for AI agents rather th
|
|
|
1584
2103
|
3. Content will be tailored to your specific technology stack and architecture
|
|
1585
2104
|
4. Documents will provide accurate, up-to-date guidance for development
|
|
1586
2105
|
|
|
1587
|
-
These steering documents provide instructions for AI agents to analyze your project and generate appropriate guidance, eliminating the language-dependency issues of template-based approaches
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
2106
|
+
These steering documents provide instructions for AI agents to analyze your project and generate appropriate guidance, eliminating the language-dependency issues of template-based approaches.`,
|
|
2107
|
+
},
|
|
2108
|
+
],
|
|
2109
|
+
};
|
|
2110
|
+
} catch (error) {
|
|
2111
|
+
return {
|
|
2112
|
+
content: [
|
|
2113
|
+
{
|
|
2114
|
+
type: "text",
|
|
2115
|
+
text: `Error creating/updating steering documents: ${error.message}`,
|
|
2116
|
+
},
|
|
2117
|
+
],
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
},
|
|
2121
|
+
);
|
|
1599
2122
|
|
|
1600
2123
|
// 12. sdd-steering-custom - Create custom steering documents
|
|
1601
|
-
server.registerTool(
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
2124
|
+
server.registerTool(
|
|
2125
|
+
"sdd-steering-custom",
|
|
2126
|
+
{
|
|
2127
|
+
title: "Create Custom Steering Document",
|
|
2128
|
+
description: "Create custom steering documents",
|
|
2129
|
+
inputSchema: {
|
|
2130
|
+
fileName: z
|
|
2131
|
+
.string()
|
|
2132
|
+
.describe(
|
|
2133
|
+
'Filename for the custom steering document (e.g., "api-standards.md")',
|
|
2134
|
+
),
|
|
2135
|
+
topic: z
|
|
2136
|
+
.string()
|
|
2137
|
+
.describe("Topic/purpose of the custom steering document"),
|
|
2138
|
+
inclusionMode: z
|
|
2139
|
+
.enum(["always", "conditional", "manual"])
|
|
2140
|
+
.describe("How this steering document should be included"),
|
|
2141
|
+
filePattern: z
|
|
2142
|
+
.string()
|
|
2143
|
+
.optional()
|
|
2144
|
+
.describe(
|
|
2145
|
+
'File pattern for conditional inclusion (e.g., "*.test.js", "src/api/**/*")',
|
|
2146
|
+
),
|
|
2147
|
+
},
|
|
1609
2148
|
},
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
2149
|
+
async ({ fileName, topic, inclusionMode, filePattern }) => {
|
|
2150
|
+
try {
|
|
2151
|
+
const currentPath = process.cwd();
|
|
2152
|
+
const steeringPath = path.join(currentPath, ".kiro", "steering");
|
|
2153
|
+
|
|
2154
|
+
// Create steering directory if it doesn't exist
|
|
2155
|
+
await fs.mkdir(steeringPath, { recursive: true });
|
|
2156
|
+
|
|
2157
|
+
// Ensure filename ends with .md
|
|
2158
|
+
if (!fileName.endsWith(".md")) {
|
|
2159
|
+
fileName += ".md";
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
// Generate inclusion mode comment
|
|
2163
|
+
let inclusionComment = "<!-- Inclusion Mode: ";
|
|
2164
|
+
if (inclusionMode === "always") {
|
|
2165
|
+
inclusionComment += "Always -->";
|
|
2166
|
+
} else if (inclusionMode === "conditional") {
|
|
2167
|
+
inclusionComment += `Conditional: "${filePattern || "**/*"}" -->`;
|
|
2168
|
+
} else {
|
|
2169
|
+
inclusionComment += "Manual -->";
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
// Generate content based on topic
|
|
2173
|
+
let content = `${inclusionComment}
|
|
2174
|
+
|
|
1636
2175
|
# ${topic}
|
|
1637
2176
|
|
|
1638
2177
|
## Purpose
|
|
1639
2178
|
This document provides specialized guidance for ${topic.toLowerCase()} within the project context.
|
|
1640
2179
|
|
|
1641
2180
|
## When This Document Applies
|
|
1642
|
-
${
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
2181
|
+
${
|
|
2182
|
+
inclusionMode === "conditional"
|
|
2183
|
+
? `This guidance applies when working with files matching: \`${filePattern || "**/*"}\``
|
|
2184
|
+
: inclusionMode === "always"
|
|
2185
|
+
? "This guidance applies to all development work in this project."
|
|
2186
|
+
: "Reference this document manually using @${fileName} when needed."
|
|
2187
|
+
}
|
|
1647
2188
|
|
|
1648
2189
|
## Guidelines
|
|
1649
2190
|
|
|
@@ -1679,545 +2220,666 @@ ${inclusionMode === 'conditional' ?
|
|
|
1679
2220
|
- [Testing requirements]
|
|
1680
2221
|
- [Review checklist items]`;
|
|
1681
2222
|
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
2223
|
+
const filePath = path.join(steeringPath, fileName);
|
|
2224
|
+
await fs.writeFile(filePath, content);
|
|
2225
|
+
|
|
2226
|
+
return {
|
|
2227
|
+
content: [
|
|
2228
|
+
{
|
|
2229
|
+
type: "text",
|
|
2230
|
+
text: `## Custom Steering Document Created
|
|
1689
2231
|
|
|
1690
2232
|
**File**: .kiro/steering/${fileName}
|
|
1691
2233
|
**Topic**: ${topic}
|
|
1692
|
-
**Inclusion Mode**: ${inclusionMode}${inclusionMode ===
|
|
2234
|
+
**Inclusion Mode**: ${inclusionMode}${inclusionMode === "conditional" ? ` (Pattern: "${filePattern}")` : ""}
|
|
1693
2235
|
|
|
1694
2236
|
**Created**: Custom steering document with template structure
|
|
1695
2237
|
**Usage**: ${
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
}
|
|
2238
|
+
inclusionMode === "always"
|
|
2239
|
+
? "Will be loaded in all AI interactions"
|
|
2240
|
+
: inclusionMode === "conditional"
|
|
2241
|
+
? `Will be loaded when working with files matching "${filePattern}"`
|
|
2242
|
+
: `Reference manually with @${fileName} when needed`
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
The document has been created with a standard template structure. Edit the file to add your specific guidelines, examples, and best practices for ${topic.toLowerCase()}.`,
|
|
2246
|
+
},
|
|
2247
|
+
],
|
|
2248
|
+
};
|
|
2249
|
+
} catch (error) {
|
|
2250
|
+
return {
|
|
2251
|
+
content: [
|
|
2252
|
+
{
|
|
2253
|
+
type: "text",
|
|
2254
|
+
text: `Error creating custom steering document: ${error.message}`,
|
|
2255
|
+
},
|
|
2256
|
+
],
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
},
|
|
2260
|
+
);
|
|
1713
2261
|
|
|
1714
2262
|
// 13. sdd-validate-design - Interactive design quality review
|
|
1715
|
-
server.registerTool(
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
2263
|
+
server.registerTool(
|
|
2264
|
+
"sdd-validate-design",
|
|
2265
|
+
{
|
|
2266
|
+
title: "Validate Design Quality",
|
|
2267
|
+
description: "Interactive design quality review and validation",
|
|
2268
|
+
inputSchema: {
|
|
2269
|
+
featureName: z.string().describe("Feature name to validate design for"),
|
|
2270
|
+
},
|
|
1720
2271
|
},
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
2272
|
+
async ({ featureName }) => {
|
|
2273
|
+
try {
|
|
2274
|
+
const currentPath = process.cwd();
|
|
2275
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
2276
|
+
const designPath = path.join(featurePath, "design.md");
|
|
2277
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
2278
|
+
|
|
2279
|
+
// Check if design document exists
|
|
2280
|
+
const designExists = await fs
|
|
2281
|
+
.access(designPath)
|
|
2282
|
+
.then(() => true)
|
|
2283
|
+
.catch(() => false);
|
|
2284
|
+
if (!designExists) {
|
|
2285
|
+
return {
|
|
2286
|
+
content: [
|
|
2287
|
+
{
|
|
2288
|
+
type: "text",
|
|
2289
|
+
text: `Error: Design document not found. Run \`sdd-design ${featureName}\` first to generate design document.`,
|
|
2290
|
+
},
|
|
2291
|
+
],
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
// Load design and spec
|
|
2296
|
+
const designContent = await fs.readFile(designPath, "utf8");
|
|
2297
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
2298
|
+
const spec = JSON.parse(specContent);
|
|
2299
|
+
|
|
2300
|
+
// Analyze design for critical issues
|
|
2301
|
+
const issues = [];
|
|
2302
|
+
|
|
2303
|
+
// Check for type safety (if TypeScript patterns detected)
|
|
2304
|
+
if (designContent.includes("any") || designContent.includes(": any")) {
|
|
2305
|
+
issues.push({
|
|
2306
|
+
title: "Type Safety Concern",
|
|
2307
|
+
concern: "Design mentions 'any' types which compromises type safety",
|
|
2308
|
+
impact: "Reduces code reliability and IDE support",
|
|
2309
|
+
suggestion:
|
|
2310
|
+
"Define explicit interfaces and types for all data structures",
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
// Check for architectural patterns
|
|
2315
|
+
if (
|
|
2316
|
+
!designContent.includes("Component") &&
|
|
2317
|
+
!designContent.includes("Service") &&
|
|
2318
|
+
!designContent.includes("Module")
|
|
2319
|
+
) {
|
|
2320
|
+
issues.push({
|
|
2321
|
+
title: "Architecture Clarity",
|
|
2322
|
+
concern: "Design lacks clear component or service boundaries",
|
|
2323
|
+
impact: "May lead to monolithic or poorly organized code",
|
|
2324
|
+
suggestion:
|
|
2325
|
+
"Define clear components, services, or modules with specific responsibilities",
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
// Check for error handling
|
|
2330
|
+
if (
|
|
2331
|
+
!designContent.includes("error") &&
|
|
2332
|
+
!designContent.includes("Error")
|
|
2333
|
+
) {
|
|
2334
|
+
issues.push({
|
|
2335
|
+
title: "Error Handling Strategy",
|
|
2336
|
+
concern: "Design doesn't address error handling patterns",
|
|
2337
|
+
impact: "Runtime errors may not be properly managed",
|
|
2338
|
+
suggestion:
|
|
2339
|
+
"Add comprehensive error handling strategy and exception management",
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
// Limit to 3 most critical issues
|
|
2344
|
+
const criticalIssues = issues.slice(0, 3);
|
|
2345
|
+
|
|
2346
|
+
// Identify design strengths
|
|
2347
|
+
const strengths = [];
|
|
2348
|
+
if (designContent.includes("mermaid") || designContent.includes("```")) {
|
|
2349
|
+
strengths.push("Visual documentation with diagrams and code examples");
|
|
2350
|
+
}
|
|
2351
|
+
if (
|
|
2352
|
+
designContent.includes("interface") ||
|
|
2353
|
+
designContent.includes("Interface")
|
|
2354
|
+
) {
|
|
2355
|
+
strengths.push("Clear interface definitions and contracts");
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
// Make GO/NO-GO decision
|
|
2359
|
+
const hasBlockingIssues =
|
|
2360
|
+
criticalIssues.length > 2 ||
|
|
2361
|
+
criticalIssues.some(
|
|
2362
|
+
(issue) =>
|
|
2363
|
+
issue.title.includes("Architecture") ||
|
|
2364
|
+
issue.title.includes("Type Safety"),
|
|
2365
|
+
);
|
|
2366
|
+
|
|
2367
|
+
const decision = hasBlockingIssues ? "NO-GO" : "GO";
|
|
2368
|
+
const nextStep =
|
|
2369
|
+
decision === "GO"
|
|
2370
|
+
? `Run \`sdd-tasks ${featureName}\` to generate implementation tasks`
|
|
2371
|
+
: `Address critical issues in design document before proceeding`;
|
|
2372
|
+
|
|
2373
|
+
let report = `## Design Validation Review: ${featureName}\n\n`;
|
|
2374
|
+
report += `**Overall Assessment**: ${decision === "GO" ? "✅ Design ready for implementation" : "❌ Design needs revision"}\n\n`;
|
|
2375
|
+
|
|
2376
|
+
if (criticalIssues.length > 0) {
|
|
2377
|
+
report += `### Critical Issues (${criticalIssues.length})\n\n`;
|
|
2378
|
+
criticalIssues.forEach((issue, index) => {
|
|
2379
|
+
report += `🔴 **Critical Issue ${index + 1}**: ${issue.title}\n`;
|
|
2380
|
+
report += `**Concern**: ${issue.concern}\n`;
|
|
2381
|
+
report += `**Impact**: ${issue.impact}\n`;
|
|
2382
|
+
report += `**Suggestion**: ${issue.suggestion}\n\n`;
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
if (strengths.length > 0) {
|
|
2387
|
+
report += `### Design Strengths\n\n`;
|
|
2388
|
+
strengths.forEach((strength) => {
|
|
2389
|
+
report += `✅ ${strength}\n`;
|
|
2390
|
+
});
|
|
2391
|
+
report += `\n`;
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
report += `### Final Assessment\n`;
|
|
2395
|
+
report += `**Decision**: ${decision}\n`;
|
|
2396
|
+
report += `**Rationale**: ${
|
|
2397
|
+
decision === "GO"
|
|
2398
|
+
? "Design addresses core requirements with acceptable architectural approach and manageable risks."
|
|
2399
|
+
: "Critical architectural or technical issues need resolution before implementation can proceed safely."
|
|
2400
|
+
}\n`;
|
|
2401
|
+
report += `**Next Steps**: ${nextStep}\n\n`;
|
|
2402
|
+
|
|
2403
|
+
report += `### Interactive Discussion\n`;
|
|
2404
|
+
report += `Questions for design review:\n`;
|
|
2405
|
+
report += `1. Do you agree with the identified issues and their severity?\n`;
|
|
2406
|
+
report += `2. Are there alternative approaches to address the concerns?\n`;
|
|
2407
|
+
report += `3. What are your thoughts on the overall design complexity?\n`;
|
|
2408
|
+
report += `4. Are there any design decisions that need clarification?\n`;
|
|
2409
|
+
|
|
1731
2410
|
return {
|
|
1732
|
-
content: [
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2411
|
+
content: [
|
|
2412
|
+
{
|
|
2413
|
+
type: "text",
|
|
2414
|
+
text: report,
|
|
2415
|
+
},
|
|
2416
|
+
],
|
|
2417
|
+
};
|
|
2418
|
+
} catch (error) {
|
|
2419
|
+
return {
|
|
2420
|
+
content: [
|
|
2421
|
+
{
|
|
2422
|
+
type: "text",
|
|
2423
|
+
text: `Error validating design: ${error.message}`,
|
|
2424
|
+
},
|
|
2425
|
+
],
|
|
1736
2426
|
};
|
|
1737
2427
|
}
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
const designContent = await fs.readFile(designPath, 'utf8');
|
|
1741
|
-
const specContent = await fs.readFile(specPath, 'utf8');
|
|
1742
|
-
const spec = JSON.parse(specContent);
|
|
1743
|
-
|
|
1744
|
-
// Analyze design for critical issues
|
|
1745
|
-
const issues = [];
|
|
1746
|
-
|
|
1747
|
-
// Check for type safety (if TypeScript patterns detected)
|
|
1748
|
-
if (designContent.includes('any') || designContent.includes(': any')) {
|
|
1749
|
-
issues.push({
|
|
1750
|
-
title: "Type Safety Concern",
|
|
1751
|
-
concern: "Design mentions 'any' types which compromises type safety",
|
|
1752
|
-
impact: "Reduces code reliability and IDE support",
|
|
1753
|
-
suggestion: "Define explicit interfaces and types for all data structures"
|
|
1754
|
-
});
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
|
-
// Check for architectural patterns
|
|
1758
|
-
if (!designContent.includes('Component') && !designContent.includes('Service') && !designContent.includes('Module')) {
|
|
1759
|
-
issues.push({
|
|
1760
|
-
title: "Architecture Clarity",
|
|
1761
|
-
concern: "Design lacks clear component or service boundaries",
|
|
1762
|
-
impact: "May lead to monolithic or poorly organized code",
|
|
1763
|
-
suggestion: "Define clear components, services, or modules with specific responsibilities"
|
|
1764
|
-
});
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
// Check for error handling
|
|
1768
|
-
if (!designContent.includes('error') && !designContent.includes('Error')) {
|
|
1769
|
-
issues.push({
|
|
1770
|
-
title: "Error Handling Strategy",
|
|
1771
|
-
concern: "Design doesn't address error handling patterns",
|
|
1772
|
-
impact: "Runtime errors may not be properly managed",
|
|
1773
|
-
suggestion: "Add comprehensive error handling strategy and exception management"
|
|
1774
|
-
});
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
// Limit to 3 most critical issues
|
|
1778
|
-
const criticalIssues = issues.slice(0, 3);
|
|
1779
|
-
|
|
1780
|
-
// Identify design strengths
|
|
1781
|
-
const strengths = [];
|
|
1782
|
-
if (designContent.includes('mermaid') || designContent.includes('```')) {
|
|
1783
|
-
strengths.push("Visual documentation with diagrams and code examples");
|
|
1784
|
-
}
|
|
1785
|
-
if (designContent.includes('interface') || designContent.includes('Interface')) {
|
|
1786
|
-
strengths.push("Clear interface definitions and contracts");
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
// Make GO/NO-GO decision
|
|
1790
|
-
const hasBlockingIssues = criticalIssues.length > 2 ||
|
|
1791
|
-
criticalIssues.some(issue => issue.title.includes('Architecture') || issue.title.includes('Type Safety'));
|
|
1792
|
-
|
|
1793
|
-
const decision = hasBlockingIssues ? 'NO-GO' : 'GO';
|
|
1794
|
-
const nextStep = decision === 'GO' ?
|
|
1795
|
-
`Run \`sdd-tasks ${featureName}\` to generate implementation tasks` :
|
|
1796
|
-
`Address critical issues in design document before proceeding`;
|
|
1797
|
-
|
|
1798
|
-
let report = `## Design Validation Review: ${featureName}\n\n`;
|
|
1799
|
-
report += `**Overall Assessment**: ${decision === 'GO' ? '✅ Design ready for implementation' : '❌ Design needs revision'}\n\n`;
|
|
1800
|
-
|
|
1801
|
-
if (criticalIssues.length > 0) {
|
|
1802
|
-
report += `### Critical Issues (${criticalIssues.length})\n\n`;
|
|
1803
|
-
criticalIssues.forEach((issue, index) => {
|
|
1804
|
-
report += `🔴 **Critical Issue ${index + 1}**: ${issue.title}\n`;
|
|
1805
|
-
report += `**Concern**: ${issue.concern}\n`;
|
|
1806
|
-
report += `**Impact**: ${issue.impact}\n`;
|
|
1807
|
-
report += `**Suggestion**: ${issue.suggestion}\n\n`;
|
|
1808
|
-
});
|
|
1809
|
-
}
|
|
1810
|
-
|
|
1811
|
-
if (strengths.length > 0) {
|
|
1812
|
-
report += `### Design Strengths\n\n`;
|
|
1813
|
-
strengths.forEach(strength => {
|
|
1814
|
-
report += `✅ ${strength}\n`;
|
|
1815
|
-
});
|
|
1816
|
-
report += `\n`;
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
report += `### Final Assessment\n`;
|
|
1820
|
-
report += `**Decision**: ${decision}\n`;
|
|
1821
|
-
report += `**Rationale**: ${
|
|
1822
|
-
decision === 'GO' ?
|
|
1823
|
-
'Design addresses core requirements with acceptable architectural approach and manageable risks.' :
|
|
1824
|
-
'Critical architectural or technical issues need resolution before implementation can proceed safely.'
|
|
1825
|
-
}\n`;
|
|
1826
|
-
report += `**Next Steps**: ${nextStep}\n\n`;
|
|
1827
|
-
|
|
1828
|
-
report += `### Interactive Discussion\n`;
|
|
1829
|
-
report += `Questions for design review:\n`;
|
|
1830
|
-
report += `1. Do you agree with the identified issues and their severity?\n`;
|
|
1831
|
-
report += `2. Are there alternative approaches to address the concerns?\n`;
|
|
1832
|
-
report += `3. What are your thoughts on the overall design complexity?\n`;
|
|
1833
|
-
report += `4. Are there any design decisions that need clarification?\n`;
|
|
1834
|
-
|
|
1835
|
-
return {
|
|
1836
|
-
content: [{
|
|
1837
|
-
type: 'text',
|
|
1838
|
-
text: report
|
|
1839
|
-
}]
|
|
1840
|
-
};
|
|
1841
|
-
} catch (error) {
|
|
1842
|
-
return {
|
|
1843
|
-
content: [{
|
|
1844
|
-
type: 'text',
|
|
1845
|
-
text: `Error validating design: ${error.message}`
|
|
1846
|
-
}]
|
|
1847
|
-
};
|
|
1848
|
-
}
|
|
1849
|
-
});
|
|
2428
|
+
},
|
|
2429
|
+
);
|
|
1850
2430
|
|
|
1851
2431
|
// 14. sdd-validate-gap - Analyze implementation gap
|
|
1852
|
-
server.registerTool(
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
2432
|
+
server.registerTool(
|
|
2433
|
+
"sdd-validate-gap",
|
|
2434
|
+
{
|
|
2435
|
+
title: "Validate Implementation Gap",
|
|
2436
|
+
description: "Analyze implementation gap between requirements and codebase",
|
|
2437
|
+
inputSchema: {
|
|
2438
|
+
featureName: z
|
|
2439
|
+
.string()
|
|
2440
|
+
.describe("Feature name to analyze implementation gap for"),
|
|
2441
|
+
},
|
|
1857
2442
|
},
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
2443
|
+
async ({ featureName }) => {
|
|
2444
|
+
try {
|
|
2445
|
+
const currentPath = process.cwd();
|
|
2446
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
2447
|
+
const requirementsPath = path.join(featurePath, "requirements.md");
|
|
2448
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
2449
|
+
|
|
2450
|
+
// Check if requirements exist
|
|
2451
|
+
const requirementsExist = await fs
|
|
2452
|
+
.access(requirementsPath)
|
|
2453
|
+
.then(() => true)
|
|
2454
|
+
.catch(() => false);
|
|
2455
|
+
if (!requirementsExist) {
|
|
2456
|
+
return {
|
|
2457
|
+
content: [
|
|
2458
|
+
{
|
|
2459
|
+
type: "text",
|
|
2460
|
+
text: `Error: Requirements document not found. Run \`sdd-requirements ${featureName}\` first to generate requirements.`,
|
|
2461
|
+
},
|
|
2462
|
+
],
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
// Load requirements and spec
|
|
2467
|
+
const requirementsContent = await fs.readFile(requirementsPath, "utf8");
|
|
2468
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
2469
|
+
const spec = JSON.parse(specContent);
|
|
2470
|
+
|
|
2471
|
+
// Analyze current codebase
|
|
2472
|
+
const codebaseAnalysis = {
|
|
2473
|
+
hasPackageJson: await fs
|
|
2474
|
+
.access("package.json")
|
|
2475
|
+
.then(() => true)
|
|
2476
|
+
.catch(() => false),
|
|
2477
|
+
hasSourceCode: false,
|
|
2478
|
+
techStack: [],
|
|
2479
|
+
architecture: "Unknown",
|
|
1873
2480
|
};
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
codebaseAnalysis.
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
2481
|
+
|
|
2482
|
+
if (codebaseAnalysis.hasPackageJson) {
|
|
2483
|
+
const packageContent = await fs.readFile("package.json", "utf8");
|
|
2484
|
+
const packageJson = JSON.parse(packageContent);
|
|
2485
|
+
codebaseAnalysis.techStack = Object.keys(
|
|
2486
|
+
packageJson.dependencies || {},
|
|
2487
|
+
);
|
|
2488
|
+
codebaseAnalysis.architecture = "Node.js Application";
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
// Check for source directories
|
|
2492
|
+
const srcExists = await fs
|
|
2493
|
+
.access("src")
|
|
2494
|
+
.then(() => true)
|
|
2495
|
+
.catch(() => false);
|
|
2496
|
+
const libExists = await fs
|
|
2497
|
+
.access("lib")
|
|
2498
|
+
.then(() => true)
|
|
2499
|
+
.catch(() => false);
|
|
2500
|
+
codebaseAnalysis.hasSourceCode = srcExists || libExists;
|
|
2501
|
+
|
|
2502
|
+
// Extract requirements complexity
|
|
2503
|
+
const requirements =
|
|
2504
|
+
requirementsContent.match(/WHEN|IF|WHILE|WHERE/g) || [];
|
|
2505
|
+
const complexity =
|
|
2506
|
+
requirements.length > 10
|
|
2507
|
+
? "XL"
|
|
2508
|
+
: requirements.length > 6
|
|
2509
|
+
? "L"
|
|
2510
|
+
: requirements.length > 3
|
|
2511
|
+
? "M"
|
|
2512
|
+
: "S";
|
|
2513
|
+
|
|
2514
|
+
// Identify gaps and implementation approaches
|
|
2515
|
+
const gaps = [];
|
|
2516
|
+
if (!codebaseAnalysis.hasSourceCode) {
|
|
2517
|
+
gaps.push(
|
|
2518
|
+
"No existing source code structure - requires full implementation from scratch",
|
|
2519
|
+
);
|
|
2520
|
+
}
|
|
2521
|
+
if (!codebaseAnalysis.techStack.includes("@modelcontextprotocol/sdk")) {
|
|
2522
|
+
gaps.push("MCP SDK integration required for AI tool compatibility");
|
|
2523
|
+
}
|
|
2524
|
+
if (
|
|
2525
|
+
requirementsContent.includes("database") ||
|
|
2526
|
+
requirementsContent.includes("storage")
|
|
2527
|
+
) {
|
|
2528
|
+
gaps.push("Data storage layer needs to be designed and implemented");
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
// Implementation strategy options
|
|
2532
|
+
const strategies = [
|
|
2533
|
+
{
|
|
2534
|
+
approach: "Extension",
|
|
2535
|
+
rationale: codebaseAnalysis.hasSourceCode
|
|
2536
|
+
? "Extend existing codebase with new functionality"
|
|
2537
|
+
: "Not applicable - no existing codebase to extend",
|
|
2538
|
+
applicable: codebaseAnalysis.hasSourceCode,
|
|
2539
|
+
complexity: codebaseAnalysis.hasSourceCode ? "M" : "N/A",
|
|
2540
|
+
tradeoffs: codebaseAnalysis.hasSourceCode
|
|
2541
|
+
? "Pros: Maintains consistency, faster development. Cons: May introduce technical debt"
|
|
2542
|
+
: "N/A",
|
|
2543
|
+
},
|
|
2544
|
+
{
|
|
2545
|
+
approach: "New Implementation",
|
|
2546
|
+
rationale: "Create new components following established patterns",
|
|
2547
|
+
applicable: true,
|
|
2548
|
+
complexity: complexity,
|
|
2549
|
+
tradeoffs:
|
|
2550
|
+
"Pros: Clean architecture, full control. Cons: More development time, integration complexity",
|
|
2551
|
+
},
|
|
2552
|
+
{
|
|
2553
|
+
approach: "Hybrid",
|
|
2554
|
+
rationale:
|
|
2555
|
+
"Combine extension of existing components with new development where needed",
|
|
2556
|
+
applicable: codebaseAnalysis.hasSourceCode,
|
|
2557
|
+
complexity: codebaseAnalysis.hasSourceCode ? "L" : "M",
|
|
2558
|
+
tradeoffs:
|
|
2559
|
+
"Pros: Balanced approach, optimal resource usage. Cons: Requires careful integration planning",
|
|
2560
|
+
},
|
|
2561
|
+
];
|
|
2562
|
+
|
|
2563
|
+
const applicableStrategies = strategies.filter((s) => s.applicable);
|
|
2564
|
+
const recommendedStrategy =
|
|
2565
|
+
applicableStrategies.find((s) => s.approach === "Hybrid") ||
|
|
2566
|
+
applicableStrategies.find((s) => s.approach === "New Implementation");
|
|
2567
|
+
|
|
2568
|
+
let report = `## Implementation Gap Analysis: ${featureName}\n\n`;
|
|
2569
|
+
|
|
2570
|
+
report += `### Analysis Summary\n`;
|
|
2571
|
+
report += `- **Feature Complexity**: ${complexity} (based on ${requirements.length} requirements)\n`;
|
|
2572
|
+
report += `- **Existing Codebase**: ${codebaseAnalysis.hasSourceCode ? "Source code detected" : "No source code structure"}\n`;
|
|
2573
|
+
report += `- **Technology Stack**: ${codebaseAnalysis.techStack.length} dependencies\n`;
|
|
2574
|
+
report += `- **Architecture Type**: ${codebaseAnalysis.architecture}\n\n`;
|
|
2575
|
+
|
|
2576
|
+
report += `### Existing Codebase Insights\n`;
|
|
2577
|
+
report += `- **Package Management**: ${codebaseAnalysis.hasPackageJson ? "npm/package.json configured" : "No package.json found"}\n`;
|
|
2578
|
+
report += `- **Source Structure**: ${codebaseAnalysis.hasSourceCode ? "Established source directories" : "No conventional source structure"}\n`;
|
|
2579
|
+
report += `- **Key Dependencies**: ${codebaseAnalysis.techStack.slice(0, 5).join(", ") || "None detected"}\n\n`;
|
|
2580
|
+
|
|
2581
|
+
if (gaps.length > 0) {
|
|
2582
|
+
report += `### Implementation Gaps Identified\n`;
|
|
2583
|
+
gaps.forEach((gap) => (report += `- ${gap}\n`));
|
|
2584
|
+
report += `\n`;
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
report += `### Implementation Strategy Options\n\n`;
|
|
2588
|
+
applicableStrategies.forEach((strategy) => {
|
|
2589
|
+
report += `**${strategy.approach} Approach**:\n`;
|
|
2590
|
+
report += `- **Rationale**: ${strategy.rationale}\n`;
|
|
2591
|
+
report += `- **Complexity**: ${strategy.complexity}\n`;
|
|
2592
|
+
report += `- **Trade-offs**: ${strategy.tradeoffs}\n\n`;
|
|
2593
|
+
});
|
|
2594
|
+
|
|
2595
|
+
report += `### Technical Research Needs\n`;
|
|
2596
|
+
const researchNeeds = [];
|
|
2597
|
+
if (!codebaseAnalysis.techStack.includes("@modelcontextprotocol/sdk")) {
|
|
2598
|
+
researchNeeds.push("MCP SDK integration patterns and best practices");
|
|
2599
|
+
}
|
|
2600
|
+
if (
|
|
2601
|
+
requirementsContent.includes("template") ||
|
|
2602
|
+
requirementsContent.includes("generation")
|
|
2603
|
+
) {
|
|
2604
|
+
researchNeeds.push("Template engine selection and implementation");
|
|
2605
|
+
}
|
|
2606
|
+
if (
|
|
2607
|
+
requirementsContent.includes("workflow") ||
|
|
2608
|
+
requirementsContent.includes("state")
|
|
2609
|
+
) {
|
|
2610
|
+
researchNeeds.push("State machine or workflow engine patterns");
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
if (researchNeeds.length > 0) {
|
|
2614
|
+
researchNeeds.forEach((need) => (report += `- ${need}\n`));
|
|
2615
|
+
} else {
|
|
2616
|
+
report += `- No significant research dependencies identified\n`;
|
|
1945
2617
|
}
|
|
1946
|
-
];
|
|
1947
|
-
|
|
1948
|
-
const applicableStrategies = strategies.filter(s => s.applicable);
|
|
1949
|
-
const recommendedStrategy = applicableStrategies.find(s => s.approach === 'Hybrid') ||
|
|
1950
|
-
applicableStrategies.find(s => s.approach === 'New Implementation');
|
|
1951
|
-
|
|
1952
|
-
let report = `## Implementation Gap Analysis: ${featureName}\n\n`;
|
|
1953
|
-
|
|
1954
|
-
report += `### Analysis Summary\n`;
|
|
1955
|
-
report += `- **Feature Complexity**: ${complexity} (based on ${requirements.length} requirements)\n`;
|
|
1956
|
-
report += `- **Existing Codebase**: ${codebaseAnalysis.hasSourceCode ? 'Source code detected' : 'No source code structure'}\n`;
|
|
1957
|
-
report += `- **Technology Stack**: ${codebaseAnalysis.techStack.length} dependencies\n`;
|
|
1958
|
-
report += `- **Architecture Type**: ${codebaseAnalysis.architecture}\n\n`;
|
|
1959
|
-
|
|
1960
|
-
report += `### Existing Codebase Insights\n`;
|
|
1961
|
-
report += `- **Package Management**: ${codebaseAnalysis.hasPackageJson ? 'npm/package.json configured' : 'No package.json found'}\n`;
|
|
1962
|
-
report += `- **Source Structure**: ${codebaseAnalysis.hasSourceCode ? 'Established source directories' : 'No conventional source structure'}\n`;
|
|
1963
|
-
report += `- **Key Dependencies**: ${codebaseAnalysis.techStack.slice(0, 5).join(', ') || 'None detected'}\n\n`;
|
|
1964
|
-
|
|
1965
|
-
if (gaps.length > 0) {
|
|
1966
|
-
report += `### Implementation Gaps Identified\n`;
|
|
1967
|
-
gaps.forEach(gap => report += `- ${gap}\n`);
|
|
1968
2618
|
report += `\n`;
|
|
2619
|
+
|
|
2620
|
+
report += `### Recommendations for Design Phase\n`;
|
|
2621
|
+
report += `- **Preferred Approach**: ${recommendedStrategy.approach} (${recommendedStrategy.complexity} complexity)\n`;
|
|
2622
|
+
report += `- **Key Decisions**: Architecture patterns, technology integration, component boundaries\n`;
|
|
2623
|
+
report += `- **Risk Mitigation**: ${complexity === "XL" || complexity === "L" ? "Consider phased implementation approach" : "Standard development approach acceptable"}\n`;
|
|
2624
|
+
report += `- **Next Step**: Use this analysis to inform technical design decisions\n`;
|
|
2625
|
+
|
|
2626
|
+
return {
|
|
2627
|
+
content: [
|
|
2628
|
+
{
|
|
2629
|
+
type: "text",
|
|
2630
|
+
text: report,
|
|
2631
|
+
},
|
|
2632
|
+
],
|
|
2633
|
+
};
|
|
2634
|
+
} catch (error) {
|
|
2635
|
+
return {
|
|
2636
|
+
content: [
|
|
2637
|
+
{
|
|
2638
|
+
type: "text",
|
|
2639
|
+
text: `Error analyzing implementation gap: ${error.message}`,
|
|
2640
|
+
},
|
|
2641
|
+
],
|
|
2642
|
+
};
|
|
1969
2643
|
}
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
applicableStrategies.forEach(strategy => {
|
|
1973
|
-
report += `**${strategy.approach} Approach**:\n`;
|
|
1974
|
-
report += `- **Rationale**: ${strategy.rationale}\n`;
|
|
1975
|
-
report += `- **Complexity**: ${strategy.complexity}\n`;
|
|
1976
|
-
report += `- **Trade-offs**: ${strategy.tradeoffs}\n\n`;
|
|
1977
|
-
});
|
|
1978
|
-
|
|
1979
|
-
report += `### Technical Research Needs\n`;
|
|
1980
|
-
const researchNeeds = [];
|
|
1981
|
-
if (!codebaseAnalysis.techStack.includes('@modelcontextprotocol/sdk')) {
|
|
1982
|
-
researchNeeds.push("MCP SDK integration patterns and best practices");
|
|
1983
|
-
}
|
|
1984
|
-
if (requirementsContent.includes('template') || requirementsContent.includes('generation')) {
|
|
1985
|
-
researchNeeds.push("Template engine selection and implementation");
|
|
1986
|
-
}
|
|
1987
|
-
if (requirementsContent.includes('workflow') || requirementsContent.includes('state')) {
|
|
1988
|
-
researchNeeds.push("State machine or workflow engine patterns");
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
if (researchNeeds.length > 0) {
|
|
1992
|
-
researchNeeds.forEach(need => report += `- ${need}\n`);
|
|
1993
|
-
} else {
|
|
1994
|
-
report += `- No significant research dependencies identified\n`;
|
|
1995
|
-
}
|
|
1996
|
-
report += `\n`;
|
|
1997
|
-
|
|
1998
|
-
report += `### Recommendations for Design Phase\n`;
|
|
1999
|
-
report += `- **Preferred Approach**: ${recommendedStrategy.approach} (${recommendedStrategy.complexity} complexity)\n`;
|
|
2000
|
-
report += `- **Key Decisions**: Architecture patterns, technology integration, component boundaries\n`;
|
|
2001
|
-
report += `- **Risk Mitigation**: ${complexity === 'XL' || complexity === 'L' ? 'Consider phased implementation approach' : 'Standard development approach acceptable'}\n`;
|
|
2002
|
-
report += `- **Next Step**: Use this analysis to inform technical design decisions\n`;
|
|
2003
|
-
|
|
2004
|
-
return {
|
|
2005
|
-
content: [{
|
|
2006
|
-
type: 'text',
|
|
2007
|
-
text: report
|
|
2008
|
-
}]
|
|
2009
|
-
};
|
|
2010
|
-
} catch (error) {
|
|
2011
|
-
return {
|
|
2012
|
-
content: [{
|
|
2013
|
-
type: 'text',
|
|
2014
|
-
text: `Error analyzing implementation gap: ${error.message}`
|
|
2015
|
-
}]
|
|
2016
|
-
};
|
|
2017
|
-
}
|
|
2018
|
-
});
|
|
2644
|
+
},
|
|
2645
|
+
);
|
|
2019
2646
|
|
|
2020
2647
|
// 15. sdd-spec-impl - Execute spec tasks using TDD
|
|
2021
|
-
server.registerTool(
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2648
|
+
server.registerTool(
|
|
2649
|
+
"sdd-spec-impl",
|
|
2650
|
+
{
|
|
2651
|
+
title: "Execute Spec Tasks with TDD",
|
|
2652
|
+
description: "Execute spec tasks using TDD methodology",
|
|
2653
|
+
inputSchema: {
|
|
2654
|
+
featureName: z.string().describe("Feature name to execute tasks for"),
|
|
2655
|
+
taskNumbers: z
|
|
2656
|
+
.string()
|
|
2657
|
+
.optional()
|
|
2658
|
+
.describe(
|
|
2659
|
+
'Specific task numbers to execute (e.g., "1.1,2.3" or leave empty for all pending)',
|
|
2660
|
+
),
|
|
2661
|
+
},
|
|
2027
2662
|
},
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
const spec = JSON.parse(specContent);
|
|
2061
|
-
|
|
2062
|
-
// Parse tasks to find pending ones
|
|
2063
|
-
const taskLines = tasksContent.split('\n');
|
|
2064
|
-
const tasks = [];
|
|
2065
|
-
let currentMajorTask = null;
|
|
2066
|
-
|
|
2067
|
-
for (let i = 0; i < taskLines.length; i++) {
|
|
2068
|
-
const line = taskLines[i].trim();
|
|
2069
|
-
|
|
2070
|
-
// Match major tasks (- [ ] 1. Task name)
|
|
2071
|
-
const majorMatch = line.match(/^- \[([ x])\] (\d+)\. (.+)$/);
|
|
2072
|
-
if (majorMatch) {
|
|
2073
|
-
currentMajorTask = {
|
|
2074
|
-
number: majorMatch[2],
|
|
2075
|
-
description: majorMatch[3],
|
|
2076
|
-
completed: majorMatch[1] === 'x',
|
|
2077
|
-
subtasks: []
|
|
2078
|
-
};
|
|
2079
|
-
tasks.push(currentMajorTask);
|
|
2080
|
-
continue;
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
// Match sub-tasks (- [ ] 1.1 Subtask name)
|
|
2084
|
-
const subMatch = line.match(/^- \[([ x])\] (\d+\.\d+) (.+)$/);
|
|
2085
|
-
if (subMatch && currentMajorTask) {
|
|
2086
|
-
currentMajorTask.subtasks.push({
|
|
2087
|
-
number: subMatch[2],
|
|
2088
|
-
description: subMatch[3],
|
|
2089
|
-
completed: subMatch[1] === 'x'
|
|
2090
|
-
});
|
|
2663
|
+
async ({ featureName, taskNumbers }) => {
|
|
2664
|
+
try {
|
|
2665
|
+
const currentPath = process.cwd();
|
|
2666
|
+
const featurePath = path.join(currentPath, ".kiro", "specs", featureName);
|
|
2667
|
+
const tasksPath = path.join(featurePath, "tasks.md");
|
|
2668
|
+
const requirementsPath = path.join(featurePath, "requirements.md");
|
|
2669
|
+
const designPath = path.join(featurePath, "design.md");
|
|
2670
|
+
const specPath = path.join(featurePath, "spec.json");
|
|
2671
|
+
|
|
2672
|
+
// Validate required files exist
|
|
2673
|
+
const requiredFiles = [
|
|
2674
|
+
{ path: requirementsPath, name: "requirements.md" },
|
|
2675
|
+
{ path: designPath, name: "design.md" },
|
|
2676
|
+
{ path: tasksPath, name: "tasks.md" },
|
|
2677
|
+
{ path: specPath, name: "spec.json" },
|
|
2678
|
+
];
|
|
2679
|
+
|
|
2680
|
+
for (const file of requiredFiles) {
|
|
2681
|
+
const exists = await fs
|
|
2682
|
+
.access(file.path)
|
|
2683
|
+
.then(() => true)
|
|
2684
|
+
.catch(() => false);
|
|
2685
|
+
if (!exists) {
|
|
2686
|
+
return {
|
|
2687
|
+
content: [
|
|
2688
|
+
{
|
|
2689
|
+
type: "text",
|
|
2690
|
+
text: `Error: Required file ${file.name} not found. Complete the full spec workflow first:\n1. sdd-requirements ${featureName}\n2. sdd-design ${featureName}\n3. sdd-tasks ${featureName}`,
|
|
2691
|
+
},
|
|
2692
|
+
],
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2091
2695
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2696
|
+
|
|
2697
|
+
// Load all context documents
|
|
2698
|
+
const tasksContent = await fs.readFile(tasksPath, "utf8");
|
|
2699
|
+
const specContent = await fs.readFile(specPath, "utf8");
|
|
2700
|
+
const spec = JSON.parse(specContent);
|
|
2701
|
+
|
|
2702
|
+
// Parse tasks to find pending ones
|
|
2703
|
+
const taskLines = tasksContent.split("\n");
|
|
2704
|
+
const tasks = [];
|
|
2705
|
+
let currentMajorTask = null;
|
|
2706
|
+
|
|
2707
|
+
for (let i = 0; i < taskLines.length; i++) {
|
|
2708
|
+
const line = taskLines[i].trim();
|
|
2709
|
+
|
|
2710
|
+
// Match major tasks (- [ ] 1. Task name)
|
|
2711
|
+
const majorMatch = line.match(/^- \[([ x])\] (\d+)\. (.+)$/);
|
|
2712
|
+
if (majorMatch) {
|
|
2713
|
+
currentMajorTask = {
|
|
2714
|
+
number: majorMatch[2],
|
|
2715
|
+
description: majorMatch[3],
|
|
2716
|
+
completed: majorMatch[1] === "x",
|
|
2717
|
+
subtasks: [],
|
|
2718
|
+
};
|
|
2719
|
+
tasks.push(currentMajorTask);
|
|
2720
|
+
continue;
|
|
2101
2721
|
}
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2722
|
+
|
|
2723
|
+
// Match sub-tasks (- [ ] 1.1 Subtask name)
|
|
2724
|
+
const subMatch = line.match(/^- \[([ x])\] (\d+\.\d+) (.+)$/);
|
|
2725
|
+
if (subMatch && currentMajorTask) {
|
|
2726
|
+
currentMajorTask.subtasks.push({
|
|
2727
|
+
number: subMatch[2],
|
|
2728
|
+
description: subMatch[3],
|
|
2729
|
+
completed: subMatch[1] === "x",
|
|
2730
|
+
});
|
|
2106
2731
|
}
|
|
2107
2732
|
}
|
|
2108
|
-
|
|
2109
|
-
//
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2733
|
+
|
|
2734
|
+
// Filter tasks based on taskNumbers parameter
|
|
2735
|
+
let tasksToExecute = [];
|
|
2736
|
+
if (taskNumbers) {
|
|
2737
|
+
const requestedNumbers = taskNumbers.split(",").map((n) => n.trim());
|
|
2738
|
+
for (const task of tasks) {
|
|
2739
|
+
if (requestedNumbers.includes(task.number) && !task.completed) {
|
|
2740
|
+
tasksToExecute.push({ type: "major", task });
|
|
2741
|
+
}
|
|
2742
|
+
for (const subtask of task.subtasks) {
|
|
2743
|
+
if (
|
|
2744
|
+
requestedNumbers.includes(subtask.number) &&
|
|
2745
|
+
!subtask.completed
|
|
2746
|
+
) {
|
|
2747
|
+
tasksToExecute.push({
|
|
2748
|
+
type: "subtask",
|
|
2749
|
+
task: subtask,
|
|
2750
|
+
parent: task,
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2113
2754
|
}
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2755
|
+
} else {
|
|
2756
|
+
// Get all pending tasks
|
|
2757
|
+
for (const task of tasks) {
|
|
2758
|
+
if (!task.completed) {
|
|
2759
|
+
tasksToExecute.push({ type: "major", task });
|
|
2760
|
+
}
|
|
2761
|
+
for (const subtask of task.subtasks) {
|
|
2762
|
+
if (!subtask.completed) {
|
|
2763
|
+
tasksToExecute.push({
|
|
2764
|
+
type: "subtask",
|
|
2765
|
+
task: subtask,
|
|
2766
|
+
parent: task,
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2117
2769
|
}
|
|
2118
2770
|
}
|
|
2119
2771
|
}
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2772
|
+
|
|
2773
|
+
if (tasksToExecute.length === 0) {
|
|
2774
|
+
return {
|
|
2775
|
+
content: [
|
|
2776
|
+
{
|
|
2777
|
+
type: "text",
|
|
2778
|
+
text: `## No Pending Tasks Found\n\n**Feature**: ${featureName}\n**Status**: ${taskNumbers ? "Specified tasks already completed or not found" : "All tasks already completed"}\n\n${taskNumbers ? `**Requested**: ${taskNumbers}` : "**All tasks**: ✅ Completed"}\n\nUse \`sdd-status ${featureName}\` to check current progress.`,
|
|
2779
|
+
},
|
|
2780
|
+
],
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
// Generate TDD implementation guidance for the tasks
|
|
2785
|
+
let report = `## TDD Implementation Execution: ${featureName}\n\n`;
|
|
2786
|
+
report += `**Tasks to Execute**: ${tasksToExecute.length} pending tasks\n`;
|
|
2787
|
+
report += `**TDD Methodology**: Kent Beck's Red → Green → Refactor cycle\n\n`;
|
|
2788
|
+
|
|
2789
|
+
report += `### Context Loaded\n`;
|
|
2790
|
+
report += `- ✅ Requirements: .kiro/specs/${featureName}/requirements.md\n`;
|
|
2791
|
+
report += `- ✅ Design: .kiro/specs/${featureName}/design.md\n`;
|
|
2792
|
+
report += `- ✅ Tasks: .kiro/specs/${featureName}/tasks.md\n`;
|
|
2793
|
+
report += `- ✅ Metadata: .kiro/specs/${featureName}/spec.json\n\n`;
|
|
2794
|
+
|
|
2795
|
+
report += `### TDD Implementation Plan\n\n`;
|
|
2796
|
+
|
|
2797
|
+
tasksToExecute.slice(0, 5).forEach((item, index) => {
|
|
2798
|
+
const task = item.task;
|
|
2799
|
+
const taskNumber = item.type === "subtask" ? task.number : task.number;
|
|
2800
|
+
const taskDesc = task.description;
|
|
2801
|
+
|
|
2802
|
+
report += `**Task ${taskNumber}**: ${taskDesc}\n\n`;
|
|
2803
|
+
report += `**TDD Cycle for this task**:\n`;
|
|
2804
|
+
report += `1. 🔴 **RED**: Write failing tests for "${taskDesc}"\n`;
|
|
2805
|
+
report += ` - Define test cases that specify expected behavior\n`;
|
|
2806
|
+
report += ` - Ensure tests fail initially (no implementation yet)\n`;
|
|
2807
|
+
report += ` - Verify test framework and setup is working\n\n`;
|
|
2808
|
+
|
|
2809
|
+
report += `2. 🟢 **GREEN**: Write minimal code to pass tests\n`;
|
|
2810
|
+
report += ` - Implement only what's needed to make tests pass\n`;
|
|
2811
|
+
report += ` - Focus on making it work, not making it perfect\n`;
|
|
2812
|
+
report += ` - Avoid over-engineering at this stage\n\n`;
|
|
2813
|
+
|
|
2814
|
+
report += `3. 🔵 **REFACTOR**: Clean up and improve code structure\n`;
|
|
2815
|
+
report += ` - Improve code quality while keeping tests green\n`;
|
|
2816
|
+
report += ` - Remove duplication and improve naming\n`;
|
|
2817
|
+
report += ` - Ensure code follows project conventions\n\n`;
|
|
2818
|
+
|
|
2819
|
+
report += `4. ✅ **VERIFY**: Complete task verification\n`;
|
|
2820
|
+
report += ` - All tests pass (new and existing)\n`;
|
|
2821
|
+
report += ` - Code quality meets standards\n`;
|
|
2822
|
+
report += ` - No regressions in existing functionality\n`;
|
|
2823
|
+
report += ` - Mark task as completed: \`- [x] ${taskNumber} ${taskDesc}\`\n\n`;
|
|
2824
|
+
});
|
|
2825
|
+
|
|
2826
|
+
if (tasksToExecute.length > 5) {
|
|
2827
|
+
report += `... and ${tasksToExecute.length - 5} more tasks\n\n`;
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
report += `### Implementation Guidelines\n`;
|
|
2831
|
+
report += `- **Follow design specifications**: Implement exactly what's specified in design.md\n`;
|
|
2832
|
+
report += `- **Test-first approach**: Always write tests before implementation code\n`;
|
|
2833
|
+
report += `- **Incremental progress**: Complete one task fully before moving to next\n`;
|
|
2834
|
+
report += `- **Update task status**: Mark checkboxes as completed in tasks.md\n`;
|
|
2835
|
+
report += `- **Quality focus**: Maintain code quality and test coverage\n\n`;
|
|
2836
|
+
|
|
2837
|
+
report += `### Next Steps\n`;
|
|
2838
|
+
report += `1. Start with the first pending task: "${tasksToExecute[0].task.description}"\n`;
|
|
2839
|
+
report += `2. Follow the TDD cycle: Red → Green → Refactor → Verify\n`;
|
|
2840
|
+
report += `3. Update tasks.md to mark completed tasks with [x]\n`;
|
|
2841
|
+
report += `4. Run \`sdd-quality-check\` to validate code quality\n`;
|
|
2842
|
+
report += `5. Continue with remaining tasks sequentially\n\n`;
|
|
2843
|
+
|
|
2844
|
+
report += `**Remember**: TDD is about building confidence through tests. Write tests that clearly specify the expected behavior, then implement the simplest solution that makes those tests pass.`;
|
|
2845
|
+
|
|
2123
2846
|
return {
|
|
2124
|
-
content: [
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2847
|
+
content: [
|
|
2848
|
+
{
|
|
2849
|
+
type: "text",
|
|
2850
|
+
text: report,
|
|
2851
|
+
},
|
|
2852
|
+
],
|
|
2853
|
+
};
|
|
2854
|
+
} catch (error) {
|
|
2855
|
+
return {
|
|
2856
|
+
content: [
|
|
2857
|
+
{
|
|
2858
|
+
type: "text",
|
|
2859
|
+
text: `Error executing spec implementation: ${error.message}`,
|
|
2860
|
+
},
|
|
2861
|
+
],
|
|
2128
2862
|
};
|
|
2129
2863
|
}
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
let report = `## TDD Implementation Execution: ${featureName}\n\n`;
|
|
2133
|
-
report += `**Tasks to Execute**: ${tasksToExecute.length} pending tasks\n`;
|
|
2134
|
-
report += `**TDD Methodology**: Kent Beck's Red → Green → Refactor cycle\n\n`;
|
|
2135
|
-
|
|
2136
|
-
report += `### Context Loaded\n`;
|
|
2137
|
-
report += `- ✅ Requirements: .kiro/specs/${featureName}/requirements.md\n`;
|
|
2138
|
-
report += `- ✅ Design: .kiro/specs/${featureName}/design.md\n`;
|
|
2139
|
-
report += `- ✅ Tasks: .kiro/specs/${featureName}/tasks.md\n`;
|
|
2140
|
-
report += `- ✅ Metadata: .kiro/specs/${featureName}/spec.json\n\n`;
|
|
2141
|
-
|
|
2142
|
-
report += `### TDD Implementation Plan\n\n`;
|
|
2143
|
-
|
|
2144
|
-
tasksToExecute.slice(0, 5).forEach((item, index) => {
|
|
2145
|
-
const task = item.task;
|
|
2146
|
-
const taskNumber = item.type === 'subtask' ? task.number : task.number;
|
|
2147
|
-
const taskDesc = task.description;
|
|
2148
|
-
|
|
2149
|
-
report += `**Task ${taskNumber}**: ${taskDesc}\n\n`;
|
|
2150
|
-
report += `**TDD Cycle for this task**:\n`;
|
|
2151
|
-
report += `1. 🔴 **RED**: Write failing tests for "${taskDesc}"\n`;
|
|
2152
|
-
report += ` - Define test cases that specify expected behavior\n`;
|
|
2153
|
-
report += ` - Ensure tests fail initially (no implementation yet)\n`;
|
|
2154
|
-
report += ` - Verify test framework and setup is working\n\n`;
|
|
2155
|
-
|
|
2156
|
-
report += `2. 🟢 **GREEN**: Write minimal code to pass tests\n`;
|
|
2157
|
-
report += ` - Implement only what's needed to make tests pass\n`;
|
|
2158
|
-
report += ` - Focus on making it work, not making it perfect\n`;
|
|
2159
|
-
report += ` - Avoid over-engineering at this stage\n\n`;
|
|
2160
|
-
|
|
2161
|
-
report += `3. 🔵 **REFACTOR**: Clean up and improve code structure\n`;
|
|
2162
|
-
report += ` - Improve code quality while keeping tests green\n`;
|
|
2163
|
-
report += ` - Remove duplication and improve naming\n`;
|
|
2164
|
-
report += ` - Ensure code follows project conventions\n\n`;
|
|
2165
|
-
|
|
2166
|
-
report += `4. ✅ **VERIFY**: Complete task verification\n`;
|
|
2167
|
-
report += ` - All tests pass (new and existing)\n`;
|
|
2168
|
-
report += ` - Code quality meets standards\n`;
|
|
2169
|
-
report += ` - No regressions in existing functionality\n`;
|
|
2170
|
-
report += ` - Mark task as completed: \`- [x] ${taskNumber} ${taskDesc}\`\n\n`;
|
|
2171
|
-
});
|
|
2172
|
-
|
|
2173
|
-
if (tasksToExecute.length > 5) {
|
|
2174
|
-
report += `... and ${tasksToExecute.length - 5} more tasks\n\n`;
|
|
2175
|
-
}
|
|
2176
|
-
|
|
2177
|
-
report += `### Implementation Guidelines\n`;
|
|
2178
|
-
report += `- **Follow design specifications**: Implement exactly what's specified in design.md\n`;
|
|
2179
|
-
report += `- **Test-first approach**: Always write tests before implementation code\n`;
|
|
2180
|
-
report += `- **Incremental progress**: Complete one task fully before moving to next\n`;
|
|
2181
|
-
report += `- **Update task status**: Mark checkboxes as completed in tasks.md\n`;
|
|
2182
|
-
report += `- **Quality focus**: Maintain code quality and test coverage\n\n`;
|
|
2183
|
-
|
|
2184
|
-
report += `### Next Steps\n`;
|
|
2185
|
-
report += `1. Start with the first pending task: "${tasksToExecute[0].task.description}"\n`;
|
|
2186
|
-
report += `2. Follow the TDD cycle: Red → Green → Refactor → Verify\n`;
|
|
2187
|
-
report += `3. Update tasks.md to mark completed tasks with [x]\n`;
|
|
2188
|
-
report += `4. Run \`sdd-quality-check\` to validate code quality\n`;
|
|
2189
|
-
report += `5. Continue with remaining tasks sequentially\n\n`;
|
|
2190
|
-
|
|
2191
|
-
report += `**Remember**: TDD is about building confidence through tests. Write tests that clearly specify the expected behavior, then implement the simplest solution that makes those tests pass.`;
|
|
2192
|
-
|
|
2193
|
-
return {
|
|
2194
|
-
content: [{
|
|
2195
|
-
type: 'text',
|
|
2196
|
-
text: report
|
|
2197
|
-
}]
|
|
2198
|
-
};
|
|
2199
|
-
} catch (error) {
|
|
2200
|
-
return {
|
|
2201
|
-
content: [{
|
|
2202
|
-
type: 'text',
|
|
2203
|
-
text: `Error executing spec implementation: ${error.message}`
|
|
2204
|
-
}]
|
|
2205
|
-
};
|
|
2206
|
-
}
|
|
2207
|
-
});
|
|
2864
|
+
},
|
|
2865
|
+
);
|
|
2208
2866
|
|
|
2209
2867
|
const transport = new StdioServerTransport();
|
|
2210
2868
|
// Helper functions for validation
|
|
2211
2869
|
function isAnalysisInsufficient(analysis) {
|
|
2212
|
-
return
|
|
2213
|
-
|
|
2214
|
-
|
|
2870
|
+
return (
|
|
2871
|
+
analysis.name === "Unknown Project" &&
|
|
2872
|
+
analysis.description === "No description available" &&
|
|
2873
|
+
analysis.dependencies.length === 0
|
|
2874
|
+
);
|
|
2215
2875
|
}
|
|
2216
2876
|
|
|
2217
2877
|
function contentContainsGenericPlaceholders(content) {
|
|
2218
|
-
return
|
|
2219
|
-
|
|
2220
|
-
|
|
2878
|
+
return (
|
|
2879
|
+
content.includes("Unknown Project") ||
|
|
2880
|
+
content.includes("No description available") ||
|
|
2881
|
+
content.includes("unknown")
|
|
2882
|
+
);
|
|
2221
2883
|
}
|
|
2222
2884
|
|
|
2223
2885
|
await server.connect(transport);
|