sdd-mcp-server 1.4.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -9
- package/dist/__tests__/legacy/setup.d.ts +44 -0
- package/dist/__tests__/legacy/setup.js +178 -0
- package/dist/__tests__/legacy/setup.js.map +1 -0
- package/dist/__tests__/legacy/test-helpers/mock-factories.d.ts +26 -0
- package/dist/__tests__/legacy/test-helpers/mock-factories.js +466 -0
- package/dist/__tests__/legacy/test-helpers/mock-factories.js.map +1 -0
- package/dist/adapters/cli/SDDToolAdapter.d.ts +12 -9
- package/dist/adapters/cli/SDDToolAdapter.js +344 -462
- package/dist/adapters/cli/SDDToolAdapter.js.map +1 -1
- package/dist/application/services/RequirementsClarificationService.d.ts +73 -0
- package/dist/application/services/RequirementsClarificationService.js +523 -0
- package/dist/application/services/RequirementsClarificationService.js.map +1 -0
- package/dist/application/services/staticSteering.d.ts +6 -0
- package/dist/application/services/staticSteering.js +567 -0
- package/dist/application/services/staticSteering.js.map +1 -0
- package/dist/domain/types.d.ts +47 -0
- package/dist/domain/types.js +8 -0
- package/dist/domain/types.js.map +1 -1
- package/dist/index.js +118 -17
- package/dist/index.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 +2030 -1324
- package/package.json +1 -1
|
@@ -11,15 +11,17 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
11
11
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
12
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
13
|
};
|
|
14
|
-
import { injectable, inject } from
|
|
15
|
-
import { TYPES } from
|
|
16
|
-
import { ProjectService } from
|
|
17
|
-
import { WorkflowService } from
|
|
18
|
-
import { TemplateService } from
|
|
19
|
-
import { QualityService } from
|
|
20
|
-
import { SteeringDocumentService } from
|
|
21
|
-
import { CodebaseAnalysisService } from
|
|
22
|
-
import {
|
|
14
|
+
import { injectable, inject } from "inversify";
|
|
15
|
+
import { TYPES } from "../../infrastructure/di/types.js";
|
|
16
|
+
import { ProjectService } from "../../application/services/ProjectService.js";
|
|
17
|
+
import { WorkflowService } from "../../application/services/WorkflowService.js";
|
|
18
|
+
import { TemplateService } from "../../application/services/TemplateService.js";
|
|
19
|
+
import { QualityService } from "../../application/services/QualityService.js";
|
|
20
|
+
import { SteeringDocumentService } from "../../application/services/SteeringDocumentService.js";
|
|
21
|
+
import { CodebaseAnalysisService } from "../../application/services/CodebaseAnalysisService.js";
|
|
22
|
+
import { RequirementsClarificationService, } from "../../application/services/RequirementsClarificationService.js";
|
|
23
|
+
import { WorkflowPhase } from "../../domain/types.js";
|
|
24
|
+
import { ensureStaticSteeringDocuments } from "../../application/services/staticSteering.js";
|
|
23
25
|
let SDDToolAdapter = class SDDToolAdapter {
|
|
24
26
|
projectService;
|
|
25
27
|
workflowService;
|
|
@@ -27,179 +29,262 @@ let SDDToolAdapter = class SDDToolAdapter {
|
|
|
27
29
|
qualityService;
|
|
28
30
|
steeringService;
|
|
29
31
|
codebaseAnalysisService;
|
|
32
|
+
clarificationService;
|
|
30
33
|
logger;
|
|
31
|
-
constructor(projectService, workflowService, templateService, qualityService, steeringService, codebaseAnalysisService, logger) {
|
|
34
|
+
constructor(projectService, workflowService, templateService, qualityService, steeringService, codebaseAnalysisService, clarificationService, logger) {
|
|
32
35
|
this.projectService = projectService;
|
|
33
36
|
this.workflowService = workflowService;
|
|
34
37
|
this.templateService = templateService;
|
|
35
38
|
this.qualityService = qualityService;
|
|
36
39
|
this.steeringService = steeringService;
|
|
37
40
|
this.codebaseAnalysisService = codebaseAnalysisService;
|
|
41
|
+
this.clarificationService = clarificationService;
|
|
38
42
|
this.logger = logger;
|
|
39
43
|
}
|
|
40
44
|
getSDDTools() {
|
|
41
45
|
return [
|
|
42
46
|
{
|
|
43
|
-
name:
|
|
47
|
+
name: "sdd-init",
|
|
44
48
|
tool: {
|
|
45
|
-
name:
|
|
46
|
-
description:
|
|
49
|
+
name: "sdd-init",
|
|
50
|
+
description: "Initialize a new SDD project with interactive requirements clarification",
|
|
47
51
|
inputSchema: {
|
|
48
|
-
type:
|
|
52
|
+
type: "object",
|
|
49
53
|
properties: {
|
|
50
|
-
|
|
54
|
+
projectName: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "The name of the project to initialize",
|
|
57
|
+
},
|
|
58
|
+
description: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "Project description",
|
|
61
|
+
},
|
|
62
|
+
clarificationAnswers: {
|
|
63
|
+
type: "object",
|
|
64
|
+
description: "Answers to clarification questions (second pass)",
|
|
65
|
+
additionalProperties: { type: "string" },
|
|
66
|
+
},
|
|
51
67
|
},
|
|
52
|
-
required: [
|
|
53
|
-
}
|
|
68
|
+
required: ["projectName"],
|
|
69
|
+
},
|
|
54
70
|
},
|
|
55
|
-
handler: this.handleProjectInit.bind(this)
|
|
71
|
+
handler: this.handleProjectInit.bind(this),
|
|
56
72
|
},
|
|
57
73
|
{
|
|
58
|
-
name:
|
|
74
|
+
name: "sdd-status",
|
|
59
75
|
tool: {
|
|
60
|
-
name:
|
|
61
|
-
description:
|
|
76
|
+
name: "sdd-status",
|
|
77
|
+
description: "Get current project status and workflow phase information",
|
|
62
78
|
inputSchema: {
|
|
63
|
-
type:
|
|
79
|
+
type: "object",
|
|
64
80
|
properties: {
|
|
65
|
-
projectId: { type:
|
|
66
|
-
projectPath: {
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
projectId: { type: "string", description: "Project ID" },
|
|
82
|
+
projectPath: {
|
|
83
|
+
type: "string",
|
|
84
|
+
description: "Project path (alternative to ID)",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
69
88
|
},
|
|
70
|
-
handler: this.handleProjectStatus.bind(this)
|
|
89
|
+
handler: this.handleProjectStatus.bind(this),
|
|
71
90
|
},
|
|
72
91
|
{
|
|
73
|
-
name:
|
|
92
|
+
name: "sdd-requirements",
|
|
74
93
|
tool: {
|
|
75
|
-
name:
|
|
76
|
-
description:
|
|
94
|
+
name: "sdd-requirements",
|
|
95
|
+
description: "Generate requirements doc",
|
|
77
96
|
inputSchema: {
|
|
78
|
-
type:
|
|
97
|
+
type: "object",
|
|
79
98
|
properties: {
|
|
80
|
-
featureName: { type:
|
|
99
|
+
featureName: { type: "string", description: "Feature name" },
|
|
81
100
|
},
|
|
82
|
-
required: [
|
|
83
|
-
}
|
|
101
|
+
required: ["featureName"],
|
|
102
|
+
},
|
|
84
103
|
},
|
|
85
|
-
handler: this.handleRequirements.bind(this)
|
|
104
|
+
handler: this.handleRequirements.bind(this),
|
|
86
105
|
},
|
|
87
106
|
{
|
|
88
|
-
name:
|
|
107
|
+
name: "sdd-design",
|
|
89
108
|
tool: {
|
|
90
|
-
name:
|
|
91
|
-
description:
|
|
109
|
+
name: "sdd-design",
|
|
110
|
+
description: "Create design specifications",
|
|
92
111
|
inputSchema: {
|
|
93
|
-
type:
|
|
112
|
+
type: "object",
|
|
94
113
|
properties: {
|
|
95
|
-
featureName: { type:
|
|
114
|
+
featureName: { type: "string", description: "Feature name" },
|
|
96
115
|
},
|
|
97
|
-
required: [
|
|
98
|
-
}
|
|
116
|
+
required: ["featureName"],
|
|
117
|
+
},
|
|
99
118
|
},
|
|
100
|
-
handler: this.handleDesign.bind(this)
|
|
119
|
+
handler: this.handleDesign.bind(this),
|
|
101
120
|
},
|
|
102
121
|
{
|
|
103
|
-
name:
|
|
122
|
+
name: "sdd-tasks",
|
|
104
123
|
tool: {
|
|
105
|
-
name:
|
|
106
|
-
description:
|
|
124
|
+
name: "sdd-tasks",
|
|
125
|
+
description: "Generate task breakdown",
|
|
107
126
|
inputSchema: {
|
|
108
|
-
type:
|
|
127
|
+
type: "object",
|
|
109
128
|
properties: {
|
|
110
|
-
featureName: { type:
|
|
129
|
+
featureName: { type: "string", description: "Feature name" },
|
|
111
130
|
},
|
|
112
|
-
required: [
|
|
113
|
-
}
|
|
131
|
+
required: ["featureName"],
|
|
132
|
+
},
|
|
114
133
|
},
|
|
115
|
-
handler: this.handleTasks.bind(this)
|
|
134
|
+
handler: this.handleTasks.bind(this),
|
|
116
135
|
},
|
|
117
136
|
{
|
|
118
|
-
name:
|
|
137
|
+
name: "sdd-quality-check",
|
|
119
138
|
tool: {
|
|
120
|
-
name:
|
|
121
|
-
description:
|
|
139
|
+
name: "sdd-quality-check",
|
|
140
|
+
description: "Perform Linus-style code quality analysis",
|
|
122
141
|
inputSchema: {
|
|
123
|
-
type:
|
|
142
|
+
type: "object",
|
|
124
143
|
properties: {
|
|
125
|
-
code: { type:
|
|
126
|
-
language: { type:
|
|
144
|
+
code: { type: "string", description: "Code to analyze" },
|
|
145
|
+
language: { type: "string", description: "Programming language" },
|
|
127
146
|
},
|
|
128
|
-
required: [
|
|
129
|
-
}
|
|
147
|
+
required: ["code"],
|
|
148
|
+
},
|
|
130
149
|
},
|
|
131
|
-
handler: this.handleQualityCheck.bind(this)
|
|
150
|
+
handler: this.handleQualityCheck.bind(this),
|
|
132
151
|
},
|
|
133
152
|
{
|
|
134
|
-
name:
|
|
153
|
+
name: "sdd-steering",
|
|
135
154
|
tool: {
|
|
136
|
-
name:
|
|
137
|
-
description:
|
|
155
|
+
name: "sdd-steering",
|
|
156
|
+
description: "Create/update steering documents with project-specific analysis",
|
|
138
157
|
inputSchema: {
|
|
139
|
-
type:
|
|
158
|
+
type: "object",
|
|
140
159
|
properties: {
|
|
141
|
-
updateMode: {
|
|
142
|
-
|
|
143
|
-
|
|
160
|
+
updateMode: {
|
|
161
|
+
type: "string",
|
|
162
|
+
enum: ["create", "update"],
|
|
163
|
+
description: "Whether to create new or update existing documents",
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
144
167
|
},
|
|
145
|
-
handler: this.handleSteering.bind(this)
|
|
168
|
+
handler: this.handleSteering.bind(this),
|
|
146
169
|
},
|
|
147
170
|
{
|
|
148
|
-
name:
|
|
171
|
+
name: "sdd-steering-custom",
|
|
149
172
|
tool: {
|
|
150
|
-
name:
|
|
151
|
-
description:
|
|
173
|
+
name: "sdd-steering-custom",
|
|
174
|
+
description: "Create custom steering documents for specialized contexts",
|
|
152
175
|
inputSchema: {
|
|
153
|
-
type:
|
|
176
|
+
type: "object",
|
|
154
177
|
properties: {
|
|
155
|
-
fileName: {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
178
|
+
fileName: {
|
|
179
|
+
type: "string",
|
|
180
|
+
description: "Filename for the custom steering document",
|
|
181
|
+
},
|
|
182
|
+
topic: {
|
|
183
|
+
type: "string",
|
|
184
|
+
description: "Topic/purpose of the custom steering document",
|
|
185
|
+
},
|
|
186
|
+
inclusionMode: {
|
|
187
|
+
type: "string",
|
|
188
|
+
enum: ["always", "conditional", "manual"],
|
|
189
|
+
description: "How this steering document should be included",
|
|
190
|
+
},
|
|
191
|
+
filePattern: {
|
|
192
|
+
type: "string",
|
|
193
|
+
description: "File pattern for conditional inclusion",
|
|
194
|
+
},
|
|
159
195
|
},
|
|
160
|
-
required: [
|
|
161
|
-
}
|
|
196
|
+
required: ["fileName", "topic", "inclusionMode"],
|
|
197
|
+
},
|
|
162
198
|
},
|
|
163
|
-
handler: this.handleSteeringCustom.bind(this)
|
|
164
|
-
}
|
|
199
|
+
handler: this.handleSteeringCustom.bind(this),
|
|
200
|
+
},
|
|
165
201
|
];
|
|
166
202
|
}
|
|
167
203
|
async handleProjectInit(args) {
|
|
168
|
-
const {
|
|
169
|
-
if (typeof
|
|
170
|
-
throw new Error(
|
|
204
|
+
const { projectName, description = "", clarificationAnswers } = args;
|
|
205
|
+
if (typeof projectName !== "string") {
|
|
206
|
+
throw new Error("Invalid arguments: projectName must be a string");
|
|
207
|
+
}
|
|
208
|
+
const currentPath = process.cwd();
|
|
209
|
+
// FIRST PASS: Analyze description quality
|
|
210
|
+
if (!clarificationAnswers) {
|
|
211
|
+
const result = await this.clarificationService.analyzeDescription(description, currentPath);
|
|
212
|
+
// If clarification needed, BLOCK and return questions
|
|
213
|
+
if (result.needsClarification && result.questions) {
|
|
214
|
+
return this.formatClarificationQuestions(result.questions, result.analysis);
|
|
215
|
+
}
|
|
171
216
|
}
|
|
172
|
-
|
|
217
|
+
// SECOND PASS: Validate and synthesize enriched description
|
|
218
|
+
let enrichedDescription = description;
|
|
219
|
+
if (clarificationAnswers && typeof clarificationAnswers === "object") {
|
|
220
|
+
const result = await this.clarificationService.analyzeDescription(description, currentPath);
|
|
221
|
+
if (result.questions) {
|
|
222
|
+
const validation = this.clarificationService.validateAnswers(result.questions, clarificationAnswers);
|
|
223
|
+
if (!validation.valid) {
|
|
224
|
+
throw new Error(`Missing required answers: ${validation.missingRequired.join(", ")}`);
|
|
225
|
+
}
|
|
226
|
+
const enriched = this.clarificationService.synthesizeDescription(description, result.questions, clarificationAnswers);
|
|
227
|
+
enrichedDescription = enriched.enriched;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Create project with enriched description
|
|
231
|
+
const project = await this.projectService.createProject(projectName, currentPath, "en");
|
|
173
232
|
// Generate initial spec.json
|
|
174
233
|
const specContent = await this.templateService.generateSpecJson(project);
|
|
175
|
-
await this.templateService.writeProjectFile(project,
|
|
234
|
+
await this.templateService.writeProjectFile(project, "spec.json", specContent);
|
|
176
235
|
// Create AGENTS.md if it doesn't exist
|
|
177
|
-
await this.createAgentsFile(
|
|
178
|
-
|
|
236
|
+
await this.createAgentsFile(currentPath);
|
|
237
|
+
const clarificationNote = clarificationAnswers
|
|
238
|
+
? "\n\n✅ Requirements Clarification: Your answers have been incorporated into an enriched project description."
|
|
239
|
+
: "";
|
|
240
|
+
return `Project "${projectName}" initialized successfully\nProject ID: ${project.id}\n\nDescription:\n${enrichedDescription}${clarificationNote}`;
|
|
241
|
+
}
|
|
242
|
+
formatClarificationQuestions(questions, analysis) {
|
|
243
|
+
let output = "## Requirements Clarification Needed\n\n";
|
|
244
|
+
output +=
|
|
245
|
+
"Your project description needs more detail to ensure we build the right solution.\n\n";
|
|
246
|
+
output += `**Quality Score**: ${Math.round(analysis.qualityScore)}/100 (need 70+ to proceed)\n\n`;
|
|
247
|
+
output += "### Please answer these questions:\n\n";
|
|
248
|
+
let questionNum = 1;
|
|
249
|
+
for (const q of questions) {
|
|
250
|
+
output += `**${questionNum}. ${q.question}**${q.required ? " *(required)*" : ""}\n`;
|
|
251
|
+
if (q.examples && q.examples.length > 0) {
|
|
252
|
+
output += ` Examples:\n`;
|
|
253
|
+
for (const ex of q.examples) {
|
|
254
|
+
output += ` - ${ex}\n`;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
output += ` Answer ID: \`${q.id}\`\n\n`;
|
|
258
|
+
questionNum++;
|
|
259
|
+
}
|
|
260
|
+
output += "\n### How to Provide Answers\n\n";
|
|
261
|
+
output +=
|
|
262
|
+
"Call sdd-init again with clarificationAnswers parameter containing your answers.\n";
|
|
263
|
+
return output;
|
|
179
264
|
}
|
|
180
265
|
async handleProjectStatus(args) {
|
|
181
266
|
const { projectId, projectPath } = args;
|
|
182
267
|
let project;
|
|
183
|
-
if (projectId && typeof projectId ===
|
|
268
|
+
if (projectId && typeof projectId === "string") {
|
|
184
269
|
project = await this.projectService.getProject(projectId);
|
|
185
270
|
}
|
|
186
|
-
else if (projectPath && typeof projectPath ===
|
|
271
|
+
else if (projectPath && typeof projectPath === "string") {
|
|
187
272
|
project = await this.projectService.getProjectByPath(projectPath);
|
|
188
273
|
}
|
|
189
274
|
else {
|
|
190
|
-
throw new Error(
|
|
275
|
+
throw new Error("Either projectId or projectPath must be provided");
|
|
191
276
|
}
|
|
192
277
|
if (!project) {
|
|
193
|
-
return
|
|
278
|
+
return "Project not found";
|
|
194
279
|
}
|
|
195
280
|
const status = await this.workflowService.getWorkflowStatus(project.id);
|
|
196
281
|
if (!status) {
|
|
197
|
-
return
|
|
282
|
+
return "Unable to get workflow status";
|
|
198
283
|
}
|
|
199
284
|
let output = `Project: ${project.name}\n`;
|
|
200
285
|
output += `Current Phase: ${status.currentPhase}\n`;
|
|
201
|
-
output += `Next Phase: ${status.nextPhase ??
|
|
202
|
-
output += `Can Progress: ${status.canProgress ?
|
|
286
|
+
output += `Next Phase: ${status.nextPhase ?? "Complete"}\n`;
|
|
287
|
+
output += `Can Progress: ${status.canProgress ? "Yes" : "No"}\n`;
|
|
203
288
|
if (status.blockers && status.blockers.length > 0) {
|
|
204
289
|
output += `Blockers:\n`;
|
|
205
290
|
for (const blocker of status.blockers) {
|
|
@@ -210,12 +295,12 @@ let SDDToolAdapter = class SDDToolAdapter {
|
|
|
210
295
|
}
|
|
211
296
|
async handleRequirements(args) {
|
|
212
297
|
const { projectId } = args;
|
|
213
|
-
if (typeof projectId !==
|
|
214
|
-
throw new Error(
|
|
298
|
+
if (typeof projectId !== "string") {
|
|
299
|
+
throw new Error("Invalid argument: projectId must be a string");
|
|
215
300
|
}
|
|
216
301
|
const project = await this.projectService.getProject(projectId);
|
|
217
302
|
if (!project) {
|
|
218
|
-
throw new Error(
|
|
303
|
+
throw new Error("Project not found");
|
|
219
304
|
}
|
|
220
305
|
// Check if can transition to requirements phase
|
|
221
306
|
const validation = await this.workflowService.validatePhaseTransition(projectId, WorkflowPhase.REQUIREMENTS);
|
|
@@ -224,23 +309,23 @@ let SDDToolAdapter = class SDDToolAdapter {
|
|
|
224
309
|
}
|
|
225
310
|
// Generate requirements template
|
|
226
311
|
const content = await this.templateService.generateRequirementsTemplate(project);
|
|
227
|
-
await this.templateService.writeProjectFile(project,
|
|
312
|
+
await this.templateService.writeProjectFile(project, "requirements.md", content);
|
|
228
313
|
// Update project phase and approval status
|
|
229
314
|
await this.projectService.updateProjectPhase(projectId, WorkflowPhase.REQUIREMENTS);
|
|
230
|
-
await this.projectService.updateApprovalStatus(projectId,
|
|
315
|
+
await this.projectService.updateApprovalStatus(projectId, "requirements", {
|
|
231
316
|
generated: true,
|
|
232
|
-
approved: false
|
|
317
|
+
approved: false,
|
|
233
318
|
});
|
|
234
319
|
return `Requirements document generated for project "${project.name}"`;
|
|
235
320
|
}
|
|
236
321
|
async handleDesign(args) {
|
|
237
322
|
const { projectId } = args;
|
|
238
|
-
if (typeof projectId !==
|
|
239
|
-
throw new Error(
|
|
323
|
+
if (typeof projectId !== "string") {
|
|
324
|
+
throw new Error("Invalid argument: projectId must be a string");
|
|
240
325
|
}
|
|
241
326
|
const project = await this.projectService.getProject(projectId);
|
|
242
327
|
if (!project) {
|
|
243
|
-
throw new Error(
|
|
328
|
+
throw new Error("Project not found");
|
|
244
329
|
}
|
|
245
330
|
// Check if can transition to design phase
|
|
246
331
|
const validation = await this.workflowService.validatePhaseTransition(projectId, WorkflowPhase.DESIGN);
|
|
@@ -249,23 +334,23 @@ let SDDToolAdapter = class SDDToolAdapter {
|
|
|
249
334
|
}
|
|
250
335
|
// Generate design template
|
|
251
336
|
const content = await this.templateService.generateDesignTemplate(project);
|
|
252
|
-
await this.templateService.writeProjectFile(project,
|
|
337
|
+
await this.templateService.writeProjectFile(project, "design.md", content);
|
|
253
338
|
// Update project phase and approval status
|
|
254
339
|
await this.projectService.updateProjectPhase(projectId, WorkflowPhase.DESIGN);
|
|
255
|
-
await this.projectService.updateApprovalStatus(projectId,
|
|
340
|
+
await this.projectService.updateApprovalStatus(projectId, "design", {
|
|
256
341
|
generated: true,
|
|
257
|
-
approved: false
|
|
342
|
+
approved: false,
|
|
258
343
|
});
|
|
259
344
|
return `Design document generated for project "${project.name}"`;
|
|
260
345
|
}
|
|
261
346
|
async handleTasks(args) {
|
|
262
347
|
const { projectId } = args;
|
|
263
|
-
if (typeof projectId !==
|
|
264
|
-
throw new Error(
|
|
348
|
+
if (typeof projectId !== "string") {
|
|
349
|
+
throw new Error("Invalid argument: projectId must be a string");
|
|
265
350
|
}
|
|
266
351
|
const project = await this.projectService.getProject(projectId);
|
|
267
352
|
if (!project) {
|
|
268
|
-
throw new Error(
|
|
353
|
+
throw new Error("Project not found");
|
|
269
354
|
}
|
|
270
355
|
// Check if can transition to tasks phase
|
|
271
356
|
const validation = await this.workflowService.validatePhaseTransition(projectId, WorkflowPhase.TASKS);
|
|
@@ -274,28 +359,28 @@ let SDDToolAdapter = class SDDToolAdapter {
|
|
|
274
359
|
}
|
|
275
360
|
// Generate tasks template
|
|
276
361
|
const content = await this.templateService.generateTasksTemplate(project);
|
|
277
|
-
await this.templateService.writeProjectFile(project,
|
|
362
|
+
await this.templateService.writeProjectFile(project, "tasks.md", content);
|
|
278
363
|
// Update project phase and approval status
|
|
279
364
|
await this.projectService.updateProjectPhase(projectId, WorkflowPhase.TASKS);
|
|
280
|
-
await this.projectService.updateApprovalStatus(projectId,
|
|
365
|
+
await this.projectService.updateApprovalStatus(projectId, "tasks", {
|
|
281
366
|
generated: true,
|
|
282
|
-
approved: false
|
|
367
|
+
approved: false,
|
|
283
368
|
});
|
|
284
369
|
return `Tasks document generated for project "${project.name}"`;
|
|
285
370
|
}
|
|
286
371
|
async handleQualityCheck(args) {
|
|
287
|
-
const { code, language =
|
|
288
|
-
if (typeof code !==
|
|
289
|
-
throw new Error(
|
|
372
|
+
const { code, language = "typescript" } = args;
|
|
373
|
+
if (typeof code !== "string") {
|
|
374
|
+
throw new Error("Invalid argument: code must be a string");
|
|
290
375
|
}
|
|
291
376
|
const report = await this.qualityService.performQualityCheck({
|
|
292
377
|
code,
|
|
293
|
-
language: language
|
|
378
|
+
language: language,
|
|
294
379
|
});
|
|
295
380
|
return this.qualityService.formatQualityReport(report);
|
|
296
381
|
}
|
|
297
382
|
async handleSteering(args) {
|
|
298
|
-
const { updateMode =
|
|
383
|
+
const { updateMode = "update" } = args;
|
|
299
384
|
const projectPath = process.cwd();
|
|
300
385
|
try {
|
|
301
386
|
// Analyze the project
|
|
@@ -306,22 +391,22 @@ let SDDToolAdapter = class SDDToolAdapter {
|
|
|
306
391
|
const structureContent = await this.generateStructureSteering(analysis);
|
|
307
392
|
// Create steering documents
|
|
308
393
|
await this.steeringService.createSteeringDocument(projectPath, {
|
|
309
|
-
name:
|
|
310
|
-
type:
|
|
311
|
-
mode:
|
|
312
|
-
content: productContent
|
|
394
|
+
name: "product.md",
|
|
395
|
+
type: "PRODUCT",
|
|
396
|
+
mode: "ALWAYS",
|
|
397
|
+
content: productContent,
|
|
313
398
|
});
|
|
314
399
|
await this.steeringService.createSteeringDocument(projectPath, {
|
|
315
|
-
name:
|
|
316
|
-
type:
|
|
317
|
-
mode:
|
|
318
|
-
content: techContent
|
|
400
|
+
name: "tech.md",
|
|
401
|
+
type: "TECHNICAL",
|
|
402
|
+
mode: "ALWAYS",
|
|
403
|
+
content: techContent,
|
|
319
404
|
});
|
|
320
405
|
await this.steeringService.createSteeringDocument(projectPath, {
|
|
321
|
-
name:
|
|
322
|
-
type:
|
|
323
|
-
mode:
|
|
324
|
-
content: structureContent
|
|
406
|
+
name: "structure.md",
|
|
407
|
+
type: "STRUCTURE",
|
|
408
|
+
mode: "ALWAYS",
|
|
409
|
+
content: structureContent,
|
|
325
410
|
});
|
|
326
411
|
// Create static steering documents if they don't exist
|
|
327
412
|
await this.createStaticSteeringDocuments(projectPath);
|
|
@@ -330,11 +415,11 @@ let SDDToolAdapter = class SDDToolAdapter {
|
|
|
330
415
|
// Get project info from package.json
|
|
331
416
|
let packageJson = {};
|
|
332
417
|
try {
|
|
333
|
-
const fs = await import(
|
|
334
|
-
const path = await import(
|
|
335
|
-
const packagePath = path.join(projectPath,
|
|
418
|
+
const fs = await import("fs");
|
|
419
|
+
const path = await import("path");
|
|
420
|
+
const packagePath = path.join(projectPath, "package.json");
|
|
336
421
|
if (fs.existsSync(packagePath)) {
|
|
337
|
-
const packageContent = fs.readFileSync(packagePath,
|
|
422
|
+
const packageContent = fs.readFileSync(packagePath, "utf8");
|
|
338
423
|
packageJson = JSON.parse(packageContent);
|
|
339
424
|
}
|
|
340
425
|
}
|
|
@@ -343,31 +428,33 @@ let SDDToolAdapter = class SDDToolAdapter {
|
|
|
343
428
|
}
|
|
344
429
|
return `## Steering Documents Updated
|
|
345
430
|
|
|
346
|
-
**Project**: ${packageJson.name ||
|
|
431
|
+
**Project**: ${packageJson.name || "Unknown"}
|
|
347
432
|
**Mode**: ${updateMode}
|
|
348
433
|
|
|
349
434
|
**Updated Files**:
|
|
350
435
|
- \`.kiro/steering/product.md\` - Product overview and business context
|
|
351
|
-
- \`.kiro/steering/tech.md\` - Technology stack and development environment
|
|
436
|
+
- \`.kiro/steering/tech.md\` - Technology stack and development environment
|
|
352
437
|
- \`.kiro/steering/structure.md\` - Project organization and architectural decisions
|
|
353
438
|
|
|
354
439
|
**Analysis**:
|
|
355
440
|
- Technology stack: ${Object.keys({ ...packageJson.dependencies, ...packageJson.devDependencies }).length} dependencies detected
|
|
356
|
-
- Project type: ${packageJson.type ||
|
|
441
|
+
- Project type: ${packageJson.type || "Unknown"}
|
|
357
442
|
- Existing steering: Updated preserving customizations
|
|
358
443
|
|
|
359
444
|
These steering documents provide consistent project context for all AI interactions and spec-driven development workflows.`;
|
|
360
445
|
}
|
|
361
446
|
catch (error) {
|
|
362
|
-
this.logger.error(
|
|
447
|
+
this.logger.error("Failed to generate steering documents", error);
|
|
363
448
|
throw new Error(`Failed to generate steering documents: ${error.message}`);
|
|
364
449
|
}
|
|
365
450
|
}
|
|
366
451
|
async handleSteeringCustom(args) {
|
|
367
452
|
const { fileName, topic, inclusionMode, filePattern } = args;
|
|
368
453
|
const projectPath = process.cwd();
|
|
369
|
-
if (typeof fileName !==
|
|
370
|
-
|
|
454
|
+
if (typeof fileName !== "string" ||
|
|
455
|
+
typeof topic !== "string" ||
|
|
456
|
+
typeof inclusionMode !== "string") {
|
|
457
|
+
throw new Error("Invalid arguments: fileName, topic, and inclusionMode must be strings");
|
|
371
458
|
}
|
|
372
459
|
const content = `# ${topic}
|
|
373
460
|
|
|
@@ -382,17 +469,19 @@ Define the purpose and scope of this steering document.
|
|
|
382
469
|
Describe when and how this steering document should be applied.
|
|
383
470
|
|
|
384
471
|
## Inclusion Mode
|
|
385
|
-
Mode: ${inclusionMode}${filePattern
|
|
386
|
-
|
|
472
|
+
Mode: ${inclusionMode}${filePattern
|
|
473
|
+
? `
|
|
474
|
+
Pattern: ${filePattern}`
|
|
475
|
+
: ""}
|
|
387
476
|
|
|
388
477
|
Generated on: ${new Date().toISOString()}
|
|
389
478
|
`;
|
|
390
479
|
await this.steeringService.createSteeringDocument(projectPath, {
|
|
391
480
|
name: fileName,
|
|
392
|
-
type:
|
|
481
|
+
type: "CUSTOM",
|
|
393
482
|
mode: inclusionMode.toUpperCase(),
|
|
394
483
|
patterns: filePattern ? [filePattern] : [],
|
|
395
|
-
content
|
|
484
|
+
content,
|
|
396
485
|
});
|
|
397
486
|
return `Custom steering document "${fileName}" created successfully with ${inclusionMode} inclusion mode.`;
|
|
398
487
|
}
|
|
@@ -400,11 +489,11 @@ Generated on: ${new Date().toISOString()}
|
|
|
400
489
|
// Try to read package.json for project info
|
|
401
490
|
let packageJson = {};
|
|
402
491
|
try {
|
|
403
|
-
const fs = await import(
|
|
404
|
-
const path = await import(
|
|
405
|
-
const packagePath = path.join(process.cwd(),
|
|
492
|
+
const fs = await import("fs");
|
|
493
|
+
const path = await import("path");
|
|
494
|
+
const packagePath = path.join(process.cwd(), "package.json");
|
|
406
495
|
if (fs.existsSync(packagePath)) {
|
|
407
|
-
const packageContent = fs.readFileSync(packagePath,
|
|
496
|
+
const packageContent = fs.readFileSync(packagePath, "utf8");
|
|
408
497
|
packageJson = JSON.parse(packageContent);
|
|
409
498
|
}
|
|
410
499
|
}
|
|
@@ -412,12 +501,14 @@ Generated on: ${new Date().toISOString()}
|
|
|
412
501
|
// Ignore errors
|
|
413
502
|
}
|
|
414
503
|
return `# Product Overview
|
|
415
|
-
|
|
504
|
+
|
|
416
505
|
## Product Description
|
|
417
|
-
${packageJson.description ||
|
|
506
|
+
${packageJson.description || "No description available"}
|
|
418
507
|
|
|
419
508
|
## Core Features
|
|
420
|
-
${this.extractFeatures(packageJson, analysis)
|
|
509
|
+
${this.extractFeatures(packageJson, analysis)
|
|
510
|
+
.map((feature) => `- ${feature}`)
|
|
511
|
+
.join("\n")}
|
|
421
512
|
|
|
422
513
|
## Target Use Case
|
|
423
514
|
${this.generateTargetUseCase(packageJson)}
|
|
@@ -432,11 +523,11 @@ ${this.generateTargetUsers(packageJson)}`;
|
|
|
432
523
|
// Try to read package.json for project info
|
|
433
524
|
let packageJson = {};
|
|
434
525
|
try {
|
|
435
|
-
const fs = await import(
|
|
436
|
-
const path = await import(
|
|
437
|
-
const packagePath = path.join(process.cwd(),
|
|
526
|
+
const fs = await import("fs");
|
|
527
|
+
const path = await import("path");
|
|
528
|
+
const packagePath = path.join(process.cwd(), "package.json");
|
|
438
529
|
if (fs.existsSync(packagePath)) {
|
|
439
|
-
const packageContent = fs.readFileSync(packagePath,
|
|
530
|
+
const packageContent = fs.readFileSync(packagePath, "utf8");
|
|
440
531
|
packageJson = JSON.parse(packageContent);
|
|
441
532
|
}
|
|
442
533
|
}
|
|
@@ -449,7 +540,7 @@ ${this.generateTargetUsers(packageJson)}`;
|
|
|
449
540
|
${this.generateTechStack(packageJson, analysis)}
|
|
450
541
|
|
|
451
542
|
## Development Environment
|
|
452
|
-
- Node.js: ${packageJson.engines?.node ||
|
|
543
|
+
- Node.js: ${packageJson.engines?.node || "Unknown"}
|
|
453
544
|
- Package Manager: npm
|
|
454
545
|
|
|
455
546
|
## Key Dependencies
|
|
@@ -481,124 +572,148 @@ ${this.generateWorkflow(analysis)}`;
|
|
|
481
572
|
// Extract features from scripts
|
|
482
573
|
if (packageJson.scripts) {
|
|
483
574
|
if (packageJson.scripts.test)
|
|
484
|
-
features.push(
|
|
575
|
+
features.push("Testing framework");
|
|
485
576
|
if (packageJson.scripts.build)
|
|
486
|
-
features.push(
|
|
577
|
+
features.push("Build system");
|
|
487
578
|
if (packageJson.scripts.dev || packageJson.scripts.start)
|
|
488
|
-
features.push(
|
|
579
|
+
features.push("Development server");
|
|
489
580
|
if (packageJson.scripts.lint)
|
|
490
|
-
features.push(
|
|
581
|
+
features.push("Code linting");
|
|
491
582
|
if (packageJson.scripts.typecheck)
|
|
492
|
-
features.push(
|
|
583
|
+
features.push("Type checking");
|
|
493
584
|
}
|
|
494
585
|
// Extract features from dependencies
|
|
495
|
-
const deps = {
|
|
586
|
+
const deps = {
|
|
587
|
+
...packageJson.dependencies,
|
|
588
|
+
...packageJson.devDependencies,
|
|
589
|
+
};
|
|
496
590
|
if (deps?.express || deps?.fastify || deps?.koa)
|
|
497
|
-
features.push(
|
|
591
|
+
features.push("Web server");
|
|
498
592
|
if (deps?.react || deps?.vue || deps?.angular)
|
|
499
|
-
features.push(
|
|
593
|
+
features.push("Frontend framework");
|
|
500
594
|
if (deps?.typescript)
|
|
501
|
-
features.push(
|
|
595
|
+
features.push("TypeScript support");
|
|
502
596
|
if (deps?.jest || deps?.mocha || deps?.vitest)
|
|
503
|
-
features.push(
|
|
597
|
+
features.push("Unit testing");
|
|
504
598
|
if (deps?.eslint)
|
|
505
|
-
features.push(
|
|
506
|
-
return features.length > 0
|
|
599
|
+
features.push("Code quality enforcement");
|
|
600
|
+
return features.length > 0
|
|
601
|
+
? features
|
|
602
|
+
: ["Core functionality to be defined"];
|
|
507
603
|
}
|
|
508
604
|
generateTargetUseCase(packageJson) {
|
|
509
605
|
if (packageJson.keywords) {
|
|
510
|
-
return `This product is designed for ${packageJson.keywords.join(
|
|
606
|
+
return `This product is designed for ${packageJson.keywords.join(", ")} use cases.`;
|
|
511
607
|
}
|
|
512
|
-
return
|
|
608
|
+
return "Target use cases to be defined based on project requirements.";
|
|
513
609
|
}
|
|
514
610
|
generateValueProposition(packageJson, analysis) {
|
|
515
611
|
const features = this.extractFeatures(packageJson, analysis);
|
|
516
|
-
return features
|
|
612
|
+
return features
|
|
613
|
+
.map((feature) => `- **${feature}**: Enhanced development experience`)
|
|
614
|
+
.join("\n");
|
|
517
615
|
}
|
|
518
616
|
generateTargetUsers(packageJson) {
|
|
519
|
-
if (packageJson.keywords?.includes(
|
|
520
|
-
return
|
|
617
|
+
if (packageJson.keywords?.includes("cli")) {
|
|
618
|
+
return "- Command-line tool users\n- Developers and system administrators";
|
|
521
619
|
}
|
|
522
|
-
if (packageJson.keywords?.includes(
|
|
523
|
-
return
|
|
620
|
+
if (packageJson.keywords?.includes("api")) {
|
|
621
|
+
return "- API consumers\n- Third-party integrators";
|
|
524
622
|
}
|
|
525
|
-
return
|
|
623
|
+
return "- Primary user persona\n- Secondary user persona";
|
|
526
624
|
}
|
|
527
625
|
generateTechStack(packageJson, analysis) {
|
|
528
|
-
const deps = {
|
|
626
|
+
const deps = {
|
|
627
|
+
...packageJson.dependencies,
|
|
628
|
+
...packageJson.devDependencies,
|
|
629
|
+
};
|
|
529
630
|
const stack = [];
|
|
530
631
|
if (deps?.typescript)
|
|
531
|
-
stack.push(
|
|
632
|
+
stack.push("TypeScript");
|
|
532
633
|
if (deps?.node || packageJson.engines?.node)
|
|
533
|
-
stack.push(
|
|
634
|
+
stack.push("Node.js");
|
|
534
635
|
if (deps?.express)
|
|
535
|
-
stack.push(
|
|
636
|
+
stack.push("Express.js");
|
|
536
637
|
if (deps?.react)
|
|
537
|
-
stack.push(
|
|
638
|
+
stack.push("React");
|
|
538
639
|
if (deps?.vue)
|
|
539
|
-
stack.push(
|
|
540
|
-
return stack.length > 0
|
|
640
|
+
stack.push("Vue.js");
|
|
641
|
+
return stack.length > 0
|
|
642
|
+
? stack.join(", ")
|
|
643
|
+
: "Technology stack to be defined";
|
|
541
644
|
}
|
|
542
645
|
generateDependencyList(packageJson) {
|
|
543
646
|
const production = Object.keys(packageJson.dependencies || {});
|
|
544
647
|
const development = Object.keys(packageJson.devDependencies || {});
|
|
545
|
-
let list =
|
|
648
|
+
let list = "";
|
|
546
649
|
if (production.length > 0) {
|
|
547
|
-
list +=
|
|
548
|
-
list += production
|
|
650
|
+
list += "### Production Dependencies\n";
|
|
651
|
+
list += production
|
|
652
|
+
.slice(0, 10)
|
|
653
|
+
.map((dep) => `- ${dep}`)
|
|
654
|
+
.join("\n");
|
|
549
655
|
}
|
|
550
656
|
if (development.length > 0) {
|
|
551
|
-
list +=
|
|
552
|
-
list += development
|
|
657
|
+
list += "\n### Development Dependencies\n";
|
|
658
|
+
list += development
|
|
659
|
+
.slice(0, 10)
|
|
660
|
+
.map((dep) => `- ${dep}`)
|
|
661
|
+
.join("\n");
|
|
553
662
|
}
|
|
554
|
-
return list ||
|
|
663
|
+
return list || "Dependencies to be analyzed";
|
|
555
664
|
}
|
|
556
665
|
generateArchitecturePatterns(analysis) {
|
|
557
666
|
const patterns = [];
|
|
558
667
|
// Try to analyze directory structure from filesystem
|
|
559
668
|
try {
|
|
560
|
-
const fs = require(
|
|
669
|
+
const fs = require("fs");
|
|
561
670
|
const projectPath = process.cwd();
|
|
562
671
|
const items = fs.readdirSync(projectPath, { withFileTypes: true });
|
|
563
672
|
const directories = items
|
|
564
673
|
.filter((item) => item.isDirectory())
|
|
565
674
|
.map((item) => item.name);
|
|
566
|
-
if (directories.includes(
|
|
567
|
-
patterns.push(
|
|
568
|
-
if (directories.includes(
|
|
569
|
-
patterns.push(
|
|
570
|
-
if (directories.includes(
|
|
571
|
-
patterns.push(
|
|
675
|
+
if (directories.includes("src"))
|
|
676
|
+
patterns.push("Source code organization");
|
|
677
|
+
if (directories.includes("test") || directories.includes("__tests__"))
|
|
678
|
+
patterns.push("Test-driven development");
|
|
679
|
+
if (directories.includes("dist") || directories.includes("build"))
|
|
680
|
+
patterns.push("Build artifact separation");
|
|
572
681
|
}
|
|
573
682
|
catch (error) {
|
|
574
683
|
// Ignore filesystem errors
|
|
575
684
|
}
|
|
576
|
-
return patterns.length > 0
|
|
685
|
+
return patterns.length > 0
|
|
686
|
+
? patterns.map((p) => `- ${p}`).join("\n")
|
|
687
|
+
: "- Patterns to be defined";
|
|
577
688
|
}
|
|
578
689
|
generateQualityStandards(packageJson) {
|
|
579
690
|
const standards = [];
|
|
580
691
|
if (packageJson.scripts?.lint)
|
|
581
|
-
standards.push(
|
|
692
|
+
standards.push("Code linting with ESLint");
|
|
582
693
|
if (packageJson.scripts?.typecheck)
|
|
583
|
-
standards.push(
|
|
694
|
+
standards.push("Type checking with TypeScript");
|
|
584
695
|
if (packageJson.scripts?.test)
|
|
585
|
-
standards.push(
|
|
586
|
-
return standards.length > 0
|
|
696
|
+
standards.push("Unit testing required");
|
|
697
|
+
return standards.length > 0
|
|
698
|
+
? standards.map((s) => `- ${s}`).join("\n")
|
|
699
|
+
: "- Quality standards to be defined";
|
|
587
700
|
}
|
|
588
701
|
generateDirectoryStructure(analysis) {
|
|
589
702
|
// Try to get directory structure from filesystem
|
|
590
703
|
try {
|
|
591
|
-
const fs = require(
|
|
704
|
+
const fs = require("fs");
|
|
592
705
|
const projectPath = process.cwd();
|
|
593
706
|
const items = fs.readdirSync(projectPath, { withFileTypes: true });
|
|
594
707
|
const directories = items
|
|
595
|
-
.filter((item) => item.isDirectory() &&
|
|
708
|
+
.filter((item) => item.isDirectory() &&
|
|
709
|
+
!item.name.startsWith(".") &&
|
|
710
|
+
item.name !== "node_modules")
|
|
596
711
|
.map((item) => `- ${item.name}/`)
|
|
597
|
-
.join(
|
|
598
|
-
return directories ||
|
|
712
|
+
.join("\n");
|
|
713
|
+
return directories || "Directory structure to be analyzed";
|
|
599
714
|
}
|
|
600
715
|
catch (error) {
|
|
601
|
-
return
|
|
716
|
+
return "Directory structure to be analyzed";
|
|
602
717
|
}
|
|
603
718
|
}
|
|
604
719
|
generateNamingConventions(analysis) {
|
|
@@ -617,11 +732,11 @@ ${this.generateWorkflow(analysis)}`;
|
|
|
617
732
|
// Try to read package.json for scripts
|
|
618
733
|
let packageJson = {};
|
|
619
734
|
try {
|
|
620
|
-
const fs = require(
|
|
621
|
-
const path = require(
|
|
622
|
-
const packagePath = path.join(process.cwd(),
|
|
735
|
+
const fs = require("fs");
|
|
736
|
+
const path = require("path");
|
|
737
|
+
const packagePath = path.join(process.cwd(), "package.json");
|
|
623
738
|
if (fs.existsSync(packagePath)) {
|
|
624
|
-
const packageContent = fs.readFileSync(packagePath,
|
|
739
|
+
const packageContent = fs.readFileSync(packagePath, "utf8");
|
|
625
740
|
packageJson = JSON.parse(packageContent);
|
|
626
741
|
}
|
|
627
742
|
}
|
|
@@ -629,7 +744,7 @@ ${this.generateWorkflow(analysis)}`;
|
|
|
629
744
|
// Ignore errors
|
|
630
745
|
}
|
|
631
746
|
const scripts = packageJson.scripts || {};
|
|
632
|
-
let workflow =
|
|
747
|
+
let workflow = "## Development Commands\n";
|
|
633
748
|
if (scripts.dev)
|
|
634
749
|
workflow += `- \`npm run dev\` - Start development server\n`;
|
|
635
750
|
if (scripts.build)
|
|
@@ -641,261 +756,26 @@ ${this.generateWorkflow(analysis)}`;
|
|
|
641
756
|
return workflow;
|
|
642
757
|
}
|
|
643
758
|
async createStaticSteeringDocuments(projectPath) {
|
|
644
|
-
|
|
645
|
-
const path = await import('path');
|
|
646
|
-
// Check if linus-review.md exists, if not create it
|
|
647
|
-
const linusReviewPath = path.join(projectPath, '.kiro', 'steering', 'linus-review.md');
|
|
648
|
-
if (!fs.existsSync(linusReviewPath)) {
|
|
649
|
-
const linusReviewContent = `# Linus Torvalds Code Review Steering Document
|
|
650
|
-
|
|
651
|
-
## Role Definition
|
|
652
|
-
|
|
653
|
-
You are channeling Linus Torvalds, creator and chief architect of the Linux kernel. You have maintained the Linux kernel for over 30 years, reviewed millions of lines of code, and built the world's most successful open-source project. Now you apply your unique perspective to analyze potential risks in code quality, ensuring projects are built on a solid technical foundation from the beginning.
|
|
654
|
-
|
|
655
|
-
## Core Philosophy
|
|
656
|
-
|
|
657
|
-
**1. "Good Taste" - The First Principle**
|
|
658
|
-
"Sometimes you can look at a problem from a different angle, rewrite it to make special cases disappear and become normal cases."
|
|
659
|
-
- Classic example: Linked list deletion, optimized from 10 lines with if statements to 4 lines without conditional branches
|
|
660
|
-
- Good taste is an intuition that requires accumulated experience
|
|
661
|
-
- Eliminating edge cases is always better than adding conditional checks
|
|
662
|
-
|
|
663
|
-
**2. "Never break userspace" - The Iron Rule**
|
|
664
|
-
"We do not break userspace!"
|
|
665
|
-
- Any change that crashes existing programs is a bug, no matter how "theoretically correct"
|
|
666
|
-
- The kernel's duty is to serve users, not educate them
|
|
667
|
-
- Backward compatibility is sacred and inviolable
|
|
668
|
-
|
|
669
|
-
**3. Pragmatism - The Belief**
|
|
670
|
-
"I'm a damn pragmatist."
|
|
671
|
-
- Solve actual problems, not imagined threats
|
|
672
|
-
- Reject "theoretically perfect" but practically complex solutions like microkernels
|
|
673
|
-
- Code should serve reality, not papers
|
|
674
|
-
|
|
675
|
-
**4. Simplicity Obsession - The Standard**
|
|
676
|
-
"If you need more than 3 levels of indentation, you're screwed and should fix your program."
|
|
677
|
-
- Functions must be short and focused, do one thing and do it well
|
|
678
|
-
- C is a Spartan language, naming should be too
|
|
679
|
-
- Complexity is the root of all evil
|
|
680
|
-
|
|
681
|
-
## Communication Principles
|
|
682
|
-
|
|
683
|
-
### Basic Communication Standards
|
|
684
|
-
|
|
685
|
-
- **Expression Style**: Direct, sharp, zero nonsense. If code is garbage, call it garbage and explain why.
|
|
686
|
-
- **Technical Priority**: Criticism is always about technical issues, not personal. Don't blur technical judgment for "niceness."
|
|
687
|
-
|
|
688
|
-
### Requirements Confirmation Process
|
|
689
|
-
|
|
690
|
-
When analyzing any code or technical need, follow these steps:
|
|
691
|
-
|
|
692
|
-
#### 0. **Thinking Premise - Linus's Three Questions**
|
|
693
|
-
Before starting any analysis, ask yourself:
|
|
694
|
-
1. "Is this a real problem or imagined?" - Reject over-engineering
|
|
695
|
-
2. "Is there a simpler way?" - Always seek the simplest solution
|
|
696
|
-
3. "Will it break anything?" - Backward compatibility is the iron rule
|
|
697
|
-
|
|
698
|
-
#### 1. **Requirements Understanding**
|
|
699
|
-
Based on the existing information, understand the requirement and restate it using Linus's thinking/communication style.
|
|
700
|
-
|
|
701
|
-
#### 2. **Linus-style Problem Decomposition Thinking**
|
|
702
|
-
|
|
703
|
-
**First Layer: Data Structure Analysis**
|
|
704
|
-
"Bad programmers worry about the code. Good programmers worry about data structures."
|
|
705
|
-
|
|
706
|
-
- What is the core data? How do they relate?
|
|
707
|
-
- Where does data flow? Who owns it? Who modifies it?
|
|
708
|
-
- Is there unnecessary data copying or transformation?
|
|
709
|
-
|
|
710
|
-
**Second Layer: Special Case Identification**
|
|
711
|
-
"Good code has no special cases"
|
|
712
|
-
|
|
713
|
-
- Find all if/else branches
|
|
714
|
-
- Which are real business logic? Which are patches for bad design?
|
|
715
|
-
- Can we redesign data structures to eliminate these branches?
|
|
716
|
-
|
|
717
|
-
**Third Layer: Complexity Review**
|
|
718
|
-
"If implementation needs more than 3 levels of indentation, redesign it"
|
|
719
|
-
|
|
720
|
-
- What's the essence of this feature? (Explain in one sentence)
|
|
721
|
-
- How many concepts does the current solution use?
|
|
722
|
-
- Can it be reduced by half? Half again?
|
|
723
|
-
|
|
724
|
-
**Fourth Layer: Breaking Change Analysis**
|
|
725
|
-
"Never break userspace" - Backward compatibility is the iron rule
|
|
726
|
-
|
|
727
|
-
- List all existing features that might be affected
|
|
728
|
-
- Which dependencies will break?
|
|
729
|
-
- How to improve without breaking anything?
|
|
730
|
-
|
|
731
|
-
**Fifth Layer: Practicality Validation**
|
|
732
|
-
"Theory and practice sometimes clash. Theory loses. Every single time."
|
|
733
|
-
|
|
734
|
-
- Does this problem really exist in production?
|
|
735
|
-
- How many users actually encounter this problem?
|
|
736
|
-
- Does the solution's complexity match the problem's severity?
|
|
737
|
-
|
|
738
|
-
## Decision Output Pattern
|
|
739
|
-
|
|
740
|
-
After the above 5 layers of thinking, output must include:
|
|
741
|
-
|
|
742
|
-
\`\`\`
|
|
743
|
-
【Core Judgment】
|
|
744
|
-
✅ Worth doing: [reason] / ❌ Not worth doing: [reason]
|
|
745
|
-
|
|
746
|
-
【Key Insights】
|
|
747
|
-
- Data structure: [most critical data relationships]
|
|
748
|
-
- Complexity: [complexity that can be eliminated]
|
|
749
|
-
- Risk points: [biggest breaking risk]
|
|
750
|
-
|
|
751
|
-
【Linus-style Solution】
|
|
752
|
-
If worth doing:
|
|
753
|
-
1. First step is always simplifying data structures
|
|
754
|
-
2. Eliminate all special cases
|
|
755
|
-
3. Implement in the dumbest but clearest way
|
|
756
|
-
4. Ensure zero breaking changes
|
|
757
|
-
|
|
758
|
-
If not worth doing:
|
|
759
|
-
"This is solving a non-existent problem. The real problem is [XXX]."
|
|
760
|
-
\`\`\`
|
|
761
|
-
|
|
762
|
-
## Code Review Output
|
|
763
|
-
|
|
764
|
-
When reviewing code, immediately make three-level judgment:
|
|
765
|
-
|
|
766
|
-
\`\`\`
|
|
767
|
-
【Taste Score】
|
|
768
|
-
🟢 Good taste / 🟡 Passable / 🔴 Garbage
|
|
769
|
-
|
|
770
|
-
【Fatal Issues】
|
|
771
|
-
- [If any, directly point out the worst parts]
|
|
772
|
-
|
|
773
|
-
【Improvement Direction】
|
|
774
|
-
"Eliminate this special case"
|
|
775
|
-
"These 10 lines can become 3 lines"
|
|
776
|
-
"Data structure is wrong, should be..."
|
|
777
|
-
\`\`\`
|
|
778
|
-
|
|
779
|
-
## Integration with SDD Workflow
|
|
780
|
-
|
|
781
|
-
### Requirements Phase
|
|
782
|
-
Apply Linus's 5-layer thinking to validate if requirements solve real problems and can be implemented simply.
|
|
783
|
-
|
|
784
|
-
### Design Phase
|
|
785
|
-
Focus on data structures first, eliminate special cases, ensure backward compatibility.
|
|
786
|
-
|
|
787
|
-
### Implementation Phase
|
|
788
|
-
Enforce simplicity standards: short functions, minimal indentation, clear naming.
|
|
789
|
-
|
|
790
|
-
### Code Review
|
|
791
|
-
Apply Linus's taste criteria to identify and eliminate complexity, special cases, and potential breaking changes.
|
|
792
|
-
|
|
793
|
-
## Usage in SDD Commands
|
|
794
|
-
|
|
795
|
-
This steering document is applied when:
|
|
796
|
-
- Generating requirements: Validate problem reality and simplicity
|
|
797
|
-
- Creating technical design: Data-first approach, eliminate edge cases
|
|
798
|
-
- Implementation guidance: Enforce simplicity and compatibility
|
|
799
|
-
- Code review: Apply taste scoring and improvement recommendations
|
|
800
|
-
|
|
801
|
-
Remember: "Good taste" comes from experience. Question everything. Simplify ruthlessly. Never break userspace.
|
|
802
|
-
`;
|
|
803
|
-
await this.steeringService.createSteeringDocument(projectPath, {
|
|
804
|
-
name: 'linus-review.md',
|
|
805
|
-
type: 'LINUS_REVIEW',
|
|
806
|
-
mode: 'ALWAYS',
|
|
807
|
-
content: linusReviewContent
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
|
-
// Check if commit.md exists, if not create it
|
|
811
|
-
const commitPath = path.join(projectPath, '.kiro', 'steering', 'commit.md');
|
|
812
|
-
if (!fs.existsSync(commitPath)) {
|
|
813
|
-
const commitContent = `# Commit Message Guidelines
|
|
814
|
-
|
|
815
|
-
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.
|
|
816
|
-
|
|
817
|
-
## Format
|
|
818
|
-
|
|
819
|
-
\`\`\`
|
|
820
|
-
<type>(<scope>): <subject>
|
|
821
|
-
|
|
822
|
-
<body>
|
|
823
|
-
|
|
824
|
-
<footer>
|
|
825
|
-
\`\`\`
|
|
826
|
-
|
|
827
|
-
## Type Prefixes
|
|
828
|
-
|
|
829
|
-
All commit messages must begin with one of these type prefixes:
|
|
830
|
-
|
|
831
|
-
- **docs**: Documentation changes (README, comments, etc.)
|
|
832
|
-
- **chore**: Maintenance tasks, dependency updates, etc.
|
|
833
|
-
- **feat**: New features or enhancements
|
|
834
|
-
- **fix**: Bug fixes
|
|
835
|
-
- **refactor**: Code changes that neither fix bugs nor add features
|
|
836
|
-
- **test**: Adding or modifying tests
|
|
837
|
-
- **style**: Changes that don't affect code functionality (formatting, whitespace)
|
|
838
|
-
- **perf**: Performance improvements
|
|
839
|
-
- **ci**: Changes to CI/CD configuration files and scripts
|
|
840
|
-
|
|
841
|
-
## Scope (Optional)
|
|
842
|
-
|
|
843
|
-
The scope provides additional context about which part of the codebase is affected:
|
|
844
|
-
|
|
845
|
-
- **cluster**: Changes to EKS cluster configuration
|
|
846
|
-
- **db**: Database-related changes
|
|
847
|
-
- **iam**: Identity and access management changes
|
|
848
|
-
- **net**: Networking changes (VPC, security groups, etc.)
|
|
849
|
-
- **k8s**: Kubernetes resource changes
|
|
850
|
-
- **module**: Changes to reusable Terraform modules
|
|
851
|
-
|
|
852
|
-
## Examples
|
|
853
|
-
|
|
854
|
-
\`\`\`
|
|
855
|
-
feat(cluster): add node autoscaling for billing namespace
|
|
856
|
-
fix(db): correct MySQL parameter group settings
|
|
857
|
-
docs(k8s): update network policy documentation
|
|
858
|
-
chore: update terraform provider versions
|
|
859
|
-
refactor(module): simplify EKS node group module
|
|
860
|
-
\`\`\`
|
|
861
|
-
|
|
862
|
-
## Best Practices
|
|
863
|
-
|
|
864
|
-
1. Keep the subject line under 72 characters
|
|
865
|
-
2. Use imperative mood in the subject line ("add" not "added")
|
|
866
|
-
3. Don't end the subject line with a period
|
|
867
|
-
4. Separate subject from body with a blank line
|
|
868
|
-
5. Use the body to explain what and why, not how
|
|
869
|
-
6. Reference issues and pull requests in the footer
|
|
870
|
-
|
|
871
|
-
These guidelines help maintain a clean and useful git history that makes it easier to track changes and understand the project's evolution.
|
|
872
|
-
`;
|
|
873
|
-
await this.steeringService.createSteeringDocument(projectPath, {
|
|
874
|
-
name: 'commit.md',
|
|
875
|
-
type: 'CUSTOM',
|
|
876
|
-
mode: 'ALWAYS',
|
|
877
|
-
content: commitContent
|
|
878
|
-
});
|
|
879
|
-
}
|
|
759
|
+
await ensureStaticSteeringDocuments(projectPath, this.steeringService);
|
|
880
760
|
}
|
|
881
761
|
async createAgentsFile(projectPath) {
|
|
882
|
-
const fs = await import(
|
|
883
|
-
const path = await import(
|
|
762
|
+
const fs = await import("fs");
|
|
763
|
+
const path = await import("path");
|
|
884
764
|
// Check if AGENTS.md exists, if not create it based on CLAUDE.md
|
|
885
|
-
const agentsPath = path.join(projectPath,
|
|
765
|
+
const agentsPath = path.join(projectPath, "AGENTS.md");
|
|
886
766
|
if (!fs.existsSync(agentsPath)) {
|
|
887
767
|
// Try to read CLAUDE.md to use as template
|
|
888
|
-
const claudePath = path.join(projectPath,
|
|
889
|
-
let agentsContent =
|
|
768
|
+
const claudePath = path.join(projectPath, "CLAUDE.md");
|
|
769
|
+
let agentsContent = "";
|
|
890
770
|
if (fs.existsSync(claudePath)) {
|
|
891
771
|
// Read CLAUDE.md and adapt it for general agents
|
|
892
|
-
const claudeContent = fs.readFileSync(claudePath,
|
|
772
|
+
const claudeContent = fs.readFileSync(claudePath, "utf8");
|
|
893
773
|
agentsContent = claudeContent
|
|
894
|
-
.replace(/# Claude Code Spec-Driven Development/g,
|
|
895
|
-
.replace(/Claude Code/g,
|
|
896
|
-
.replace(/claude code/g,
|
|
897
|
-
.replace(/Claude/g,
|
|
898
|
-
.replace(/claude/g,
|
|
774
|
+
.replace(/# Claude Code Spec-Driven Development/g, "# AI Agent Spec-Driven Development")
|
|
775
|
+
.replace(/Claude Code/g, "AI Agent")
|
|
776
|
+
.replace(/claude code/g, "ai agent")
|
|
777
|
+
.replace(/Claude/g, "AI Agent")
|
|
778
|
+
.replace(/claude/g, "ai agent");
|
|
899
779
|
}
|
|
900
780
|
else {
|
|
901
781
|
// Fallback to basic template if CLAUDE.md doesn't exist
|
|
@@ -912,7 +792,7 @@ Kiro-style Spec Driven Development implementation for AI agents across different
|
|
|
912
792
|
|
|
913
793
|
### Steering vs Specification
|
|
914
794
|
|
|
915
|
-
**Steering** (\`.kiro/steering/\`) - Guide AI with project-wide rules and context
|
|
795
|
+
**Steering** (\`.kiro/steering/\`) - Guide AI with project-wide rules and context
|
|
916
796
|
**Specs** (\`.kiro/specs/\`) - Formalize development process for individual features
|
|
917
797
|
|
|
918
798
|
### Active Specifications
|
|
@@ -928,7 +808,7 @@ Kiro-style Spec Driven Development implementation for AI agents across different
|
|
|
928
808
|
## Workflow
|
|
929
809
|
|
|
930
810
|
### Phase 0: Steering (Optional)
|
|
931
|
-
Agent steering commands - Create/update steering documents
|
|
811
|
+
Agent steering commands - Create/update steering documents
|
|
932
812
|
Agent steering-custom commands - Create custom steering for specialized contexts
|
|
933
813
|
|
|
934
814
|
Note: Optional for new features or small additions. You can proceed directly to spec-init.
|
|
@@ -965,7 +845,7 @@ Managed by agent steering commands. Updates here reflect command changes.
|
|
|
965
845
|
|
|
966
846
|
### Custom Steering Files
|
|
967
847
|
<!-- Added by agent steering-custom commands -->
|
|
968
|
-
<!-- Format:
|
|
848
|
+
<!-- Format:
|
|
969
849
|
- \`filename.md\`: Mode - Pattern(s) - Description
|
|
970
850
|
Mode: Always|Conditional|Manual
|
|
971
851
|
Pattern: File patterns for Conditional mode
|
|
@@ -991,13 +871,15 @@ SDDToolAdapter = __decorate([
|
|
|
991
871
|
__param(3, inject(TYPES.QualityService)),
|
|
992
872
|
__param(4, inject(TYPES.SteeringDocumentService)),
|
|
993
873
|
__param(5, inject(TYPES.CodebaseAnalysisService)),
|
|
994
|
-
__param(6, inject(TYPES.
|
|
874
|
+
__param(6, inject(TYPES.RequirementsClarificationService)),
|
|
875
|
+
__param(7, inject(TYPES.LoggerPort)),
|
|
995
876
|
__metadata("design:paramtypes", [ProjectService,
|
|
996
877
|
WorkflowService,
|
|
997
878
|
TemplateService,
|
|
998
879
|
QualityService,
|
|
999
880
|
SteeringDocumentService,
|
|
1000
|
-
CodebaseAnalysisService,
|
|
881
|
+
CodebaseAnalysisService,
|
|
882
|
+
RequirementsClarificationService, Object])
|
|
1001
883
|
], SDDToolAdapter);
|
|
1002
884
|
export { SDDToolAdapter };
|
|
1003
885
|
//# sourceMappingURL=SDDToolAdapter.js.map
|