sdd-mcp-server 3.5.0 → 4.0.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 +97 -671
- package/agents/architect.md +15 -93
- package/agents/implementer.md +16 -141
- package/agents/planner.md +16 -84
- package/agents/reviewer.md +16 -239
- package/agents/security-auditor.md +16 -114
- package/agents/tdd-guide.md +17 -228
- package/dist/adapters/cli/SDDToolAdapter.d.ts +14 -5
- package/dist/adapters/cli/SDDToolAdapter.js +189 -362
- package/dist/adapters/cli/SDDToolAdapter.js.map +1 -1
- package/dist/application/services/ContextCompactionService.d.ts +81 -16
- package/dist/application/services/ContextCompactionService.js +370 -187
- package/dist/application/services/ContextCompactionService.js.map +1 -1
- package/dist/application/services/SpecPathResolver.d.ts +24 -0
- package/dist/application/services/SpecPathResolver.js +70 -0
- package/dist/application/services/SpecPathResolver.js.map +1 -0
- package/dist/application/services/WorkflowEngineService.d.ts +100 -46
- package/dist/application/services/WorkflowEngineService.js +468 -288
- package/dist/application/services/WorkflowEngineService.js.map +1 -1
- package/dist/cli/install-skills.d.ts +3 -9
- package/dist/cli/install-skills.js +130 -175
- package/dist/cli/install-skills.js.map +1 -1
- package/dist/cli/install-target.d.ts +45 -14
- package/dist/cli/install-target.js +26 -12
- package/dist/cli/install-target.js.map +1 -1
- package/dist/cli/sdd-mcp-cli.d.ts +1 -1
- package/dist/cli/sdd-mcp-cli.js +7 -6
- package/dist/cli/sdd-mcp-cli.js.map +1 -1
- package/dist/cli/tool-support/claude-code.js +13 -34
- package/dist/cli/tool-support/claude-code.js.map +1 -1
- package/dist/cli/tool-support/codex.d.ts +0 -53
- package/dist/cli/tool-support/codex.js +6 -94
- package/dist/cli/tool-support/codex.js.map +1 -1
- package/dist/cli/tool-support/index.d.ts +3 -2
- package/dist/cli/tool-support/index.js +3 -1
- package/dist/cli/tool-support/index.js.map +1 -1
- package/dist/cli/tool-support/omp.d.ts +5 -0
- package/dist/cli/tool-support/omp.js +43 -0
- package/dist/cli/tool-support/omp.js.map +1 -0
- package/dist/cli/tool-support/root-guidance.d.ts +2 -9
- package/dist/cli/tool-support/root-guidance.js +44 -37
- package/dist/cli/tool-support/root-guidance.js.map +1 -1
- package/dist/cli/tool-support/target-agent-renderer.d.ts +1 -0
- package/dist/cli/tool-support/target-agent-renderer.js +37 -4
- package/dist/cli/tool-support/target-agent-renderer.js.map +1 -1
- package/dist/cli/tool-support/target-installer.d.ts +8 -2
- package/dist/cli/tool-support/target-installer.js +94 -26
- package/dist/cli/tool-support/target-installer.js.map +1 -1
- package/dist/cli/utils/preserving-writer.d.ts +22 -0
- package/dist/cli/utils/preserving-writer.js +233 -11
- package/dist/cli/utils/preserving-writer.js.map +1 -1
- package/dist/domain/ports.d.ts +4 -0
- package/dist/index.d.ts +13 -10
- package/dist/index.js +16 -1199
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/adapters/NodeFileSystemAdapter.d.ts +3 -0
- package/dist/infrastructure/adapters/NodeFileSystemAdapter.js +10 -0
- package/dist/infrastructure/adapters/NodeFileSystemAdapter.js.map +1 -1
- package/dist/infrastructure/mcp/CapabilityNegotiator.js +3 -3
- package/dist/infrastructure/mcp/CapabilityNegotiator.js.map +1 -1
- package/dist/infrastructure/mcp/sddToolDefinitions.d.ts +6 -0
- package/dist/infrastructure/mcp/sddToolDefinitions.js +124 -0
- package/dist/infrastructure/mcp/sddToolDefinitions.js.map +1 -0
- package/dist/utils/atomicWrite.d.ts +8 -35
- package/dist/utils/atomicWrite.js +12 -60
- package/dist/utils/atomicWrite.js.map +1 -1
- package/mcp-server.js +5 -2883
- package/package.json +5 -2
- package/scripts/context-usage-report.mjs +602 -0
- package/sdd-entry.js +17 -6
- package/skills/sdd-commit/REFERENCE.md +31 -0
- package/skills/sdd-commit/SKILL.md +17 -273
- package/skills/sdd-design/REFERENCE.md +35 -0
- package/skills/sdd-design/SKILL.md +19 -265
- package/skills/sdd-implement/REFERENCE.md +26 -0
- package/skills/sdd-implement/SKILL.md +22 -283
- package/skills/sdd-requirements/REFERENCE.md +31 -0
- package/skills/sdd-requirements/SKILL.md +23 -135
- package/skills/sdd-review/REFERENCE.md +26 -0
- package/skills/sdd-review/SKILL.md +17 -181
- package/skills/sdd-security-check/REFERENCE.md +19 -0
- package/skills/sdd-security-check/SKILL.md +18 -184
- package/skills/sdd-steering/REFERENCE.md +25 -0
- package/skills/sdd-steering/SKILL.md +18 -216
- package/skills/sdd-steering-custom/REFERENCE.md +27 -0
- package/skills/sdd-steering-custom/SKILL.md +19 -203
- package/skills/sdd-tasks/REFERENCE.md +25 -0
- package/skills/sdd-tasks/SKILL.md +19 -248
- package/skills/sdd-test-gen/REFERENCE.md +15 -0
- package/skills/sdd-test-gen/SKILL.md +17 -287
- package/skills/simple-task/REFERENCE.md +22 -0
- package/skills/simple-task/SKILL.md +17 -138
- package/templates/CLAUDE.md +18 -30
- package/rules/git-workflow.md +0 -92
- package/rules/sdd-workflow.md +0 -116
package/dist/index.js
CHANGED
|
@@ -1,29 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const isMCPMode = process.argv[1]?.includes("sdd-mcp-server") ||
|
|
6
|
-
process.argv[0]?.includes("sdd-mcp-server") ||
|
|
7
|
-
process.env.npm_execpath?.includes("npx") || // Executed via npx
|
|
8
|
-
process.stdin.isTTY === false || // MCP servers communicate via stdio pipes
|
|
9
|
-
process.argv.includes("--mcp-mode") || // Explicit MCP mode flag
|
|
10
|
-
process.argv.includes("--simplified") || // Use simplified mode flag
|
|
11
|
-
false; // Default to full server for better functionality
|
|
12
|
-
if (isMCPMode) {
|
|
13
|
-
// Completely silence all console output for MCP mode
|
|
14
|
-
console.log = () => { };
|
|
15
|
-
console.info = () => { };
|
|
16
|
-
console.warn = () => { };
|
|
17
|
-
console.debug = () => { };
|
|
18
|
-
// Keep error for debugging but send to stderr
|
|
19
|
-
const originalError = console.error;
|
|
20
|
-
console.error = (...args) => originalError("[SDD-DEBUG]", ...args);
|
|
21
|
-
}
|
|
22
|
-
import "reflect-metadata";
|
|
23
|
-
import { existsSync } from "fs";
|
|
24
|
-
import nodePath from "path";
|
|
25
|
-
import { createContainer } from "./infrastructure/di/container.js";
|
|
26
|
-
import { TYPES } from "./infrastructure/di/types.js";
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
import { createContainer } from './infrastructure/di/container.js';
|
|
4
|
+
import { TYPES } from './infrastructure/di/types.js';
|
|
27
5
|
export async function createMCPServer() {
|
|
28
6
|
const container = createContainer();
|
|
29
7
|
const logger = container.get(TYPES.LoggerPort);
|
|
@@ -32,7 +10,6 @@ export async function createMCPServer() {
|
|
|
32
10
|
const hookSystem = container.get(TYPES.HookSystem);
|
|
33
11
|
const toolRegistry = container.get(TYPES.PluginToolRegistry);
|
|
34
12
|
const steeringRegistry = container.get(TYPES.PluginSteeringRegistry);
|
|
35
|
-
// Initialize plugin system
|
|
36
13
|
await pluginManager.initialize();
|
|
37
14
|
return {
|
|
38
15
|
container,
|
|
@@ -42,1185 +19,25 @@ export async function createMCPServer() {
|
|
|
42
19
|
hookSystem,
|
|
43
20
|
toolRegistry,
|
|
44
21
|
steeringRegistry,
|
|
45
|
-
async initialize() {
|
|
46
|
-
// Already initialized above
|
|
47
|
-
},
|
|
22
|
+
async initialize() { },
|
|
48
23
|
async close() {
|
|
49
24
|
await mcpServer.stop();
|
|
50
25
|
},
|
|
51
26
|
};
|
|
52
27
|
}
|
|
53
|
-
async function
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// Resolve version dynamically from package.json when possible
|
|
58
|
-
let pkgVersion = "0.0.0";
|
|
59
|
-
try {
|
|
60
|
-
const fs = await import("fs");
|
|
61
|
-
const path = await import("path");
|
|
62
|
-
const pkgPath = path.join(process.cwd(), "package.json");
|
|
63
|
-
if (fs.existsSync(pkgPath)) {
|
|
64
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
65
|
-
pkgVersion = pkg.version || pkgVersion;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// fall back to hardcoded default if needed
|
|
70
|
-
}
|
|
71
|
-
const server = new Server({
|
|
72
|
-
name: "sdd-mcp-server",
|
|
73
|
-
version: pkgVersion,
|
|
74
|
-
}, {
|
|
75
|
-
capabilities: {
|
|
76
|
-
tools: {},
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
// MCP Tools - Action-oriented tools only
|
|
80
|
-
// Template/guidance tools have been moved to Agent Skills (use /sdd-requirements, /sdd-design, etc.)
|
|
81
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
82
|
-
return {
|
|
83
|
-
tools: [
|
|
84
|
-
{
|
|
85
|
-
name: "sdd-init",
|
|
86
|
-
description: "Initialize a new SDD project with interactive requirements clarification",
|
|
87
|
-
inputSchema: {
|
|
88
|
-
type: "object",
|
|
89
|
-
properties: {
|
|
90
|
-
projectName: {
|
|
91
|
-
type: "string",
|
|
92
|
-
description: "The name of the project to initialize",
|
|
93
|
-
},
|
|
94
|
-
description: {
|
|
95
|
-
type: "string",
|
|
96
|
-
description: "Project description",
|
|
97
|
-
},
|
|
98
|
-
clarificationAnswers: {
|
|
99
|
-
type: "object",
|
|
100
|
-
description: "Answers to clarification questions (second pass)",
|
|
101
|
-
additionalProperties: { type: "string" },
|
|
102
|
-
},
|
|
103
|
-
reviewTestCases: {
|
|
104
|
-
type: "boolean",
|
|
105
|
-
description: "Enable an optional TDD test-case review checkpoint before implementation",
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
required: ["projectName"],
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
name: "sdd-status",
|
|
113
|
-
description: "Check workflow progress",
|
|
114
|
-
inputSchema: {
|
|
115
|
-
type: "object",
|
|
116
|
-
properties: {
|
|
117
|
-
featureName: {
|
|
118
|
-
type: "string",
|
|
119
|
-
description: "Feature name (optional - shows all if not provided)",
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
name: "sdd-approve",
|
|
126
|
-
description: "Approve workflow phases",
|
|
127
|
-
inputSchema: {
|
|
128
|
-
type: "object",
|
|
129
|
-
properties: {
|
|
130
|
-
featureName: {
|
|
131
|
-
type: "string",
|
|
132
|
-
description: "Feature name from spec initialization",
|
|
133
|
-
},
|
|
134
|
-
phase: {
|
|
135
|
-
type: "string",
|
|
136
|
-
description: "Phase to approve",
|
|
137
|
-
enum: ["requirements", "design", "tasks"],
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
required: ["featureName", "phase"],
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
name: "sdd-review-test-cases",
|
|
145
|
-
description: "Mark optional TDD test-case review checkpoint as reviewed",
|
|
146
|
-
inputSchema: {
|
|
147
|
-
type: "object",
|
|
148
|
-
properties: {
|
|
149
|
-
featureName: {
|
|
150
|
-
type: "string",
|
|
151
|
-
description: "Feature name from spec initialization",
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
required: ["featureName"],
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
name: "sdd-quality-check",
|
|
159
|
-
description: "Code quality analysis",
|
|
160
|
-
inputSchema: {
|
|
161
|
-
type: "object",
|
|
162
|
-
properties: {
|
|
163
|
-
code: {
|
|
164
|
-
type: "string",
|
|
165
|
-
description: "Code to analyze",
|
|
166
|
-
},
|
|
167
|
-
language: {
|
|
168
|
-
type: "string",
|
|
169
|
-
description: "Programming language (default: javascript)",
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
required: ["code"],
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
name: "sdd-context-load",
|
|
177
|
-
description: "Load project context",
|
|
178
|
-
inputSchema: {
|
|
179
|
-
type: "object",
|
|
180
|
-
properties: {
|
|
181
|
-
featureName: {
|
|
182
|
-
type: "string",
|
|
183
|
-
description: "Feature name to load context for",
|
|
184
|
-
},
|
|
185
|
-
mode: {
|
|
186
|
-
type: "string",
|
|
187
|
-
enum: ["compact", "standard", "full"],
|
|
188
|
-
description: "Context size mode. Defaults to compact handoff context.",
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
required: ["featureName"],
|
|
192
|
-
},
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
name: "sdd-validate-design",
|
|
196
|
-
description: "Interactive design quality review and validation",
|
|
197
|
-
inputSchema: {
|
|
198
|
-
type: "object",
|
|
199
|
-
properties: {
|
|
200
|
-
featureName: {
|
|
201
|
-
type: "string",
|
|
202
|
-
description: "Feature name to validate design for",
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
required: ["featureName"],
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
name: "sdd-validate-gap",
|
|
210
|
-
description: "Analyze implementation gap between requirements and codebase",
|
|
211
|
-
inputSchema: {
|
|
212
|
-
type: "object",
|
|
213
|
-
properties: {
|
|
214
|
-
featureName: {
|
|
215
|
-
type: "string",
|
|
216
|
-
description: "Feature name to analyze implementation gap for",
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
required: ["featureName"],
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
name: "sdd-spec-impl",
|
|
224
|
-
description: "Execute spec tasks using TDD methodology",
|
|
225
|
-
inputSchema: {
|
|
226
|
-
type: "object",
|
|
227
|
-
properties: {
|
|
228
|
-
featureName: {
|
|
229
|
-
type: "string",
|
|
230
|
-
description: "Feature name to execute tasks for",
|
|
231
|
-
},
|
|
232
|
-
taskNumbers: {
|
|
233
|
-
type: "string",
|
|
234
|
-
description: "Specific task numbers to execute (e.g., \"1.1,2.3\" or leave empty for all pending)",
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
required: ["featureName"],
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
name: "sdd-list-skills",
|
|
242
|
-
description: "List available SDD Agent Skills for Codex or Claude Code",
|
|
243
|
-
inputSchema: {
|
|
244
|
-
type: "object",
|
|
245
|
-
properties: {},
|
|
246
|
-
},
|
|
247
|
-
},
|
|
248
|
-
],
|
|
249
|
-
};
|
|
250
|
-
});
|
|
251
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
252
|
-
const { name, arguments: args } = request.params;
|
|
253
|
-
switch (name) {
|
|
254
|
-
case "sdd-init":
|
|
255
|
-
return await handleInitSimplified(args);
|
|
256
|
-
case "sdd-status":
|
|
257
|
-
return await handleStatusSimplified(args);
|
|
258
|
-
case "sdd-quality-check":
|
|
259
|
-
return await handleQualityCheckSimplified(args);
|
|
260
|
-
case "sdd-approve":
|
|
261
|
-
return await handleApproveSimplified(args);
|
|
262
|
-
case "sdd-review-test-cases":
|
|
263
|
-
return await handleReviewTestCasesSimplified(args);
|
|
264
|
-
case "sdd-context-load":
|
|
265
|
-
return await handleContextLoadSimplified(args);
|
|
266
|
-
case "sdd-validate-design":
|
|
267
|
-
return {
|
|
268
|
-
content: [
|
|
269
|
-
{
|
|
270
|
-
type: "text",
|
|
271
|
-
text: `Design validation for ${args.featureName}. (Simplified MCP mode)`,
|
|
272
|
-
},
|
|
273
|
-
],
|
|
274
|
-
};
|
|
275
|
-
case "sdd-validate-gap":
|
|
276
|
-
return {
|
|
277
|
-
content: [
|
|
278
|
-
{
|
|
279
|
-
type: "text",
|
|
280
|
-
text: `Implementation gap analysis for ${args.featureName}. (Simplified MCP mode)`,
|
|
281
|
-
},
|
|
282
|
-
],
|
|
283
|
-
};
|
|
284
|
-
case "sdd-spec-impl":
|
|
285
|
-
return {
|
|
286
|
-
content: [
|
|
287
|
-
{
|
|
288
|
-
type: "text",
|
|
289
|
-
text: `TDD implementation for ${args.featureName}. (Simplified MCP mode)`,
|
|
290
|
-
},
|
|
291
|
-
],
|
|
292
|
-
};
|
|
293
|
-
case "sdd-list-skills":
|
|
294
|
-
return await handleListSkillsSimplified();
|
|
295
|
-
default:
|
|
296
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
// Handle initialized notification
|
|
300
|
-
server.setNotificationHandler(InitializedNotificationSchema, async () => {
|
|
301
|
-
// Client has completed initialization - server is ready
|
|
302
|
-
});
|
|
303
|
-
const transport = new StdioServerTransport();
|
|
304
|
-
await server.connect(transport);
|
|
305
|
-
}
|
|
306
|
-
// List available SDD Agent Skills
|
|
307
|
-
async function handleListSkillsSimplified() {
|
|
308
|
-
const path = await import("path");
|
|
309
|
-
const { SkillManager } = await import("./skills/SkillManager.js");
|
|
310
|
-
try {
|
|
311
|
-
// Determine skills path - check multiple locations
|
|
312
|
-
const possiblePaths = [
|
|
313
|
-
path.resolve(process.cwd(), "node_modules/sdd-mcp-server/skills"),
|
|
314
|
-
path.resolve(process.cwd(), "skills"),
|
|
315
|
-
path.resolve(__dirname, "../skills"),
|
|
316
|
-
path.resolve(__dirname, "../../skills"),
|
|
317
|
-
];
|
|
318
|
-
let skillsPath = possiblePaths[0];
|
|
319
|
-
const fs = await import("fs");
|
|
320
|
-
for (const p of possiblePaths) {
|
|
321
|
-
if (fs.existsSync(p)) {
|
|
322
|
-
skillsPath = p;
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
const skillManager = new SkillManager(skillsPath);
|
|
327
|
-
const skills = await skillManager.listSkills();
|
|
328
|
-
if (skills.length === 0) {
|
|
329
|
-
return {
|
|
330
|
-
content: [
|
|
331
|
-
{
|
|
332
|
-
type: "text",
|
|
333
|
-
text: `## SDD Agent Skills
|
|
334
|
-
|
|
335
|
-
No skills found in the package. Skills may not be installed correctly.
|
|
336
|
-
|
|
337
|
-
**Expected location**: \`${skillsPath}\`
|
|
338
|
-
|
|
339
|
-
**Installation**: Run \`npx sdd-mcp-server install --target codex --skills\` or \`npx sdd-mcp-server install --target claude-code --skills\`.`,
|
|
340
|
-
},
|
|
341
|
-
],
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
const skillsList = skills
|
|
345
|
-
.map((skill) => `- **${skill.name}**: ${skill.description || "No description"}`)
|
|
346
|
-
.join("\n");
|
|
347
|
-
return {
|
|
348
|
-
content: [
|
|
349
|
-
{
|
|
350
|
-
type: "text",
|
|
351
|
-
text: `## SDD Agent Skills
|
|
352
|
-
|
|
353
|
-
Available skills for Codex or Claude Code:
|
|
354
|
-
|
|
355
|
-
${skillsList}
|
|
356
|
-
|
|
357
|
-
**Total**: ${skills.length} skills
|
|
358
|
-
|
|
359
|
-
**Installation**: Run \`npx sdd-mcp-server install --target codex --skills\` or \`npx sdd-mcp-server install --target claude-code --skills\`.
|
|
360
|
-
|
|
361
|
-
After installation, use these skills in the selected target agent:
|
|
362
|
-
- \`/sdd-requirements <feature-name>\`
|
|
363
|
-
- \`/sdd-design <feature-name>\`
|
|
364
|
-
- \`/sdd-tasks <feature-name>\`
|
|
365
|
-
- \`/sdd-implement <feature-name>\`
|
|
366
|
-
- \`/sdd-steering\`
|
|
367
|
-
- \`/sdd-commit\``,
|
|
368
|
-
},
|
|
369
|
-
],
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
catch (error) {
|
|
373
|
-
return {
|
|
374
|
-
content: [
|
|
375
|
-
{
|
|
376
|
-
type: "text",
|
|
377
|
-
text: `Error listing skills: ${error.message}`,
|
|
378
|
-
},
|
|
379
|
-
],
|
|
380
|
-
isError: true,
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Helper function to handle module loader failures consistently
|
|
386
|
-
*
|
|
387
|
-
* @param context - Description of what was being loaded (e.g., "documentGenerator", "specGenerator")
|
|
388
|
-
* @param error - The error that occurred during loading
|
|
389
|
-
* @param allowFallback - Whether to allow fallback templates (from env var or args)
|
|
390
|
-
* @returns Object with fallback content if allowed, or throws error
|
|
391
|
-
* @throws Error if fallback is not allowed
|
|
392
|
-
*/
|
|
393
|
-
function handleLoaderFailure(context, error, allowFallback = false) {
|
|
394
|
-
const errorMessage = `Failed to load ${context}: ${error.message}\n\nTo use template fallbacks, set SDD_ALLOW_TEMPLATE_FALLBACK=true environment variable or run 'npm run build' to generate required files.`;
|
|
395
|
-
console.error(`[SDD-DEBUG] Loader failure for ${context}:`, error.message);
|
|
396
|
-
console.error(`[SDD-DEBUG] Fallback allowed:`, allowFallback);
|
|
397
|
-
if (!allowFallback) {
|
|
398
|
-
// Propagate error - do not use fallback
|
|
399
|
-
throw new Error(errorMessage);
|
|
400
|
-
}
|
|
401
|
-
console.error(`[SDD-DEBUG] Using fallback templates for ${context}`);
|
|
402
|
-
return { useFallback: true, error };
|
|
403
|
-
}
|
|
404
|
-
// Status handler aligned with full server behavior
|
|
405
|
-
async function handleStatusSimplified(args) {
|
|
406
|
-
const fs = await import("fs");
|
|
407
|
-
const path = await import("path");
|
|
408
|
-
const { featureName } = args || {};
|
|
409
|
-
const currentPath = process.cwd();
|
|
410
|
-
// Support both .spec (new) and .kiro (legacy)
|
|
411
|
-
const specPath = path.join(currentPath, ".spec");
|
|
412
|
-
const kiroPath = path.join(currentPath, ".kiro");
|
|
413
|
-
const sddPath = await fs.promises.access(specPath).then(() => specPath).catch(() => kiroPath);
|
|
414
|
-
const exists = await fs.promises
|
|
415
|
-
.access(sddPath)
|
|
416
|
-
.then(() => true)
|
|
417
|
-
.catch(() => false);
|
|
418
|
-
if (!exists) {
|
|
419
|
-
return {
|
|
420
|
-
content: [
|
|
421
|
-
{
|
|
422
|
-
type: "text",
|
|
423
|
-
text: "SDD project status: No active project found. Use sdd-init to create a new project.",
|
|
424
|
-
},
|
|
425
|
-
],
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
const specsPath = path.join(sddPath, "specs");
|
|
429
|
-
if (featureName) {
|
|
430
|
-
const featurePath = path.join(specsPath, featureName);
|
|
431
|
-
const specPath = path.join(featurePath, "spec.json");
|
|
432
|
-
const specExists = await fs.promises
|
|
433
|
-
.access(specPath)
|
|
434
|
-
.then(() => true)
|
|
435
|
-
.catch(() => false);
|
|
436
|
-
if (!specExists) {
|
|
437
|
-
return {
|
|
438
|
-
content: [
|
|
439
|
-
{
|
|
440
|
-
type: "text",
|
|
441
|
-
text: `Feature "${featureName}" not found. Use sdd-init to create it.`,
|
|
442
|
-
},
|
|
443
|
-
],
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
const spec = JSON.parse(fs.readFileSync(specPath, "utf8"));
|
|
447
|
-
let status = `## SDD Project Status: ${spec.feature_name}\n\n`;
|
|
448
|
-
status += `**Current Phase**: ${spec.phase}\n`;
|
|
449
|
-
status += `**Language**: ${spec.language}\n`;
|
|
450
|
-
status += `**Created**: ${spec.created_at}\n`;
|
|
451
|
-
status += `**Updated**: ${spec.updated_at}\n\n`;
|
|
452
|
-
status += `**Phase Progress**:\n`;
|
|
453
|
-
status += `- Requirements: ${spec.approvals.requirements.generated ? "✅ Generated" : "❌ Not Generated"}${spec.approvals.requirements.approved ? ", ✅ Approved" : ", ❌ Not Approved"}\n`;
|
|
454
|
-
status += `- Design: ${spec.approvals.design.generated ? "✅ Generated" : "❌ Not Generated"}${spec.approvals.design.approved ? ", ✅ Approved" : ", ❌ Not Approved"}\n`;
|
|
455
|
-
status += `- Tasks: ${spec.approvals.tasks.generated ? "✅ Generated" : "❌ Not Generated"}${spec.approvals.tasks.approved ? ", ✅ Approved" : ", ❌ Not Approved"}\n\n`;
|
|
456
|
-
if (spec.checkpoints?.test_cases?.required) {
|
|
457
|
-
status += `**TDD Test Case Review**: ${spec.checkpoints.test_cases.reviewed ? "✅ Reviewed" : "❌ Pending"}\n`;
|
|
458
|
-
}
|
|
459
|
-
status += `**Ready for Implementation**: ${spec.ready_for_implementation ? "✅ Yes" : "❌ No"}\n\n`;
|
|
460
|
-
if (!spec.approvals.requirements.generated)
|
|
461
|
-
status += `**Next Step**: Run \`sdd-requirements ${featureName}\``;
|
|
462
|
-
else if (!spec.approvals.design.generated)
|
|
463
|
-
status += `**Next Step**: Run \`sdd-design ${featureName}\``;
|
|
464
|
-
else if (!spec.approvals.tasks.generated)
|
|
465
|
-
status += `**Next Step**: Run \`sdd-tasks ${featureName}\``;
|
|
466
|
-
else if (spec.checkpoints?.test_cases?.required && !spec.checkpoints.test_cases.reviewed)
|
|
467
|
-
status += `**Next Step**: Review test cases, then run \`sdd-review-test-cases ${featureName}\``;
|
|
468
|
-
else
|
|
469
|
-
status += `**Next Step**: Run \`sdd-implement ${featureName}\` to begin implementation`;
|
|
470
|
-
return { content: [{ type: "text", text: status }] };
|
|
471
|
-
}
|
|
472
|
-
const features = await fs.promises
|
|
473
|
-
.readdir(specsPath)
|
|
474
|
-
.catch(() => []);
|
|
475
|
-
if (features.length === 0) {
|
|
476
|
-
return {
|
|
477
|
-
content: [
|
|
478
|
-
{
|
|
479
|
-
type: "text",
|
|
480
|
-
text: "No SDD features found. Use sdd-init to create a new project.",
|
|
481
|
-
},
|
|
482
|
-
],
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
let status = "## SDD Project Status - All Features\n\n";
|
|
486
|
-
for (const feature of features) {
|
|
487
|
-
const specPath = path.join(specsPath, feature, "spec.json");
|
|
488
|
-
const specExists = await fs.promises
|
|
489
|
-
.access(specPath)
|
|
490
|
-
.then(() => true)
|
|
491
|
-
.catch(() => false);
|
|
492
|
-
if (specExists) {
|
|
493
|
-
const spec = JSON.parse(fs.readFileSync(specPath, "utf8"));
|
|
494
|
-
status += `**${spec.feature_name}**:\n`;
|
|
495
|
-
status += `- Phase: ${spec.phase}\n`;
|
|
496
|
-
status += `- Requirements: ${spec.approvals.requirements.generated ? "✅" : "❌"}\n`;
|
|
497
|
-
status += `- Design: ${spec.approvals.design.generated ? "✅" : "❌"}\n`;
|
|
498
|
-
status += `- Tasks: ${spec.approvals.tasks.generated ? "✅" : "❌"}\n`;
|
|
499
|
-
if (spec.checkpoints?.test_cases?.required) {
|
|
500
|
-
status += `- Test cases reviewed: ${spec.checkpoints.test_cases.reviewed ? "✅" : "❌"}\n`;
|
|
501
|
-
}
|
|
502
|
-
status += `- Ready: ${spec.ready_for_implementation ? "✅" : "❌"}\n\n`;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
return { content: [{ type: "text", text: status }] };
|
|
506
|
-
}
|
|
507
|
-
// Approve handler to update spec.json
|
|
508
|
-
async function handleApproveSimplified(args) {
|
|
509
|
-
const fs = await import("fs");
|
|
510
|
-
const path = await import("path");
|
|
511
|
-
const { featureName, phase } = args || {};
|
|
512
|
-
if (!featureName || !phase) {
|
|
513
|
-
return {
|
|
514
|
-
content: [{ type: "text", text: "featureName and phase are required" }],
|
|
515
|
-
isError: true,
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
try {
|
|
519
|
-
// Support both .spec (new) and .kiro (legacy)
|
|
520
|
-
const sddDir = fs.existsSync(path.join(process.cwd(), ".spec")) ? ".spec" : ".kiro";
|
|
521
|
-
const featurePath = path.join(process.cwd(), sddDir, "specs", featureName);
|
|
522
|
-
const specPath = path.join(featurePath, "spec.json");
|
|
523
|
-
const spec = JSON.parse(fs.readFileSync(specPath, "utf8"));
|
|
524
|
-
if (!spec.approvals?.[phase]?.generated) {
|
|
525
|
-
return {
|
|
526
|
-
content: [
|
|
527
|
-
{
|
|
528
|
-
type: "text",
|
|
529
|
-
text: `Error: ${phase} must be generated before approval. Run sdd-${phase} ${featureName} first.`,
|
|
530
|
-
},
|
|
531
|
-
],
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
if (phase === "tasks" &&
|
|
535
|
-
spec.checkpoints?.test_cases?.required &&
|
|
536
|
-
!spec.checkpoints.test_cases.reviewed) {
|
|
537
|
-
return {
|
|
538
|
-
content: [
|
|
539
|
-
{
|
|
540
|
-
type: "text",
|
|
541
|
-
text: `Error: TDD test cases must be reviewed before approving tasks. ` +
|
|
542
|
-
`After review, run sdd-review-test-cases ${featureName}.`,
|
|
543
|
-
},
|
|
544
|
-
],
|
|
545
|
-
isError: true,
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
spec.approvals[phase].approved = true;
|
|
549
|
-
spec.updated_at = new Date().toISOString();
|
|
550
|
-
spec.ready_for_implementation =
|
|
551
|
-
spec.approvals.requirements?.approved === true &&
|
|
552
|
-
spec.approvals.design?.approved === true &&
|
|
553
|
-
spec.approvals.tasks?.approved === true &&
|
|
554
|
-
(spec.checkpoints?.test_cases?.required !== true ||
|
|
555
|
-
spec.checkpoints.test_cases.reviewed === true);
|
|
556
|
-
fs.writeFileSync(specPath, JSON.stringify(spec, null, 2));
|
|
557
|
-
const handoff = await generateCompactHandoffSimplified(featureName, phase);
|
|
558
|
-
return {
|
|
559
|
-
content: [
|
|
560
|
-
{
|
|
561
|
-
type: "text",
|
|
562
|
-
text: `## Phase Approved\n\n` +
|
|
563
|
-
`**Feature**: ${featureName}\n` +
|
|
564
|
-
`**Phase**: ${phase}\n` +
|
|
565
|
-
`**Status**: ✅ Approved\n` +
|
|
566
|
-
`**Context Handoff**: ${handoff.relativePath}\n` +
|
|
567
|
-
`**Estimated Context Reduction**: ${handoff.reductionPercentage}% ` +
|
|
568
|
-
`(~${handoff.sourceTokens} → ~${handoff.compactTokens} tokens)`,
|
|
569
|
-
},
|
|
570
|
-
],
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
catch (error) {
|
|
574
|
-
return {
|
|
575
|
-
content: [
|
|
576
|
-
{
|
|
577
|
-
type: "text",
|
|
578
|
-
text: `Error approving phase: ${error.message}`,
|
|
579
|
-
},
|
|
580
|
-
],
|
|
581
|
-
isError: true,
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
async function handleContextLoadSimplified(args) {
|
|
586
|
-
const fs = await import("fs");
|
|
587
|
-
const path = await import("path");
|
|
588
|
-
const { featureName, mode = "compact" } = args || {};
|
|
589
|
-
if (!featureName) {
|
|
590
|
-
return {
|
|
591
|
-
content: [{ type: "text", text: "featureName is required" }],
|
|
592
|
-
isError: true,
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
if (!["compact", "standard", "full"].includes(mode)) {
|
|
596
|
-
return {
|
|
597
|
-
content: [{ type: "text", text: "mode must be compact, standard, or full" }],
|
|
598
|
-
isError: true,
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
try {
|
|
602
|
-
const { specDir, specPath } = ensureSimplifiedFeatureExists(featureName);
|
|
603
|
-
const handoffPath = path.join(specDir, "context", "handoff.md");
|
|
604
|
-
if (mode === "full") {
|
|
605
|
-
const context = await loadFullContextSimplified(featureName);
|
|
606
|
-
return { content: [{ type: "text", text: context }] };
|
|
607
|
-
}
|
|
608
|
-
if (!fs.existsSync(handoffPath)) {
|
|
609
|
-
await generateCompactHandoffSimplified(featureName, "requirements");
|
|
610
|
-
}
|
|
611
|
-
let context = fs.readFileSync(handoffPath, "utf8");
|
|
612
|
-
if (mode === "standard") {
|
|
613
|
-
context += `\n\n## Current Spec Metadata\n\n\`\`\`json\n${fs.readFileSync(specPath, "utf8")}\n\`\`\``;
|
|
614
|
-
}
|
|
615
|
-
return { content: [{ type: "text", text: context }] };
|
|
616
|
-
}
|
|
617
|
-
catch (error) {
|
|
618
|
-
return {
|
|
619
|
-
content: [
|
|
620
|
-
{
|
|
621
|
-
type: "text",
|
|
622
|
-
text: `Error loading context: ${error.message}`,
|
|
623
|
-
},
|
|
624
|
-
],
|
|
625
|
-
isError: true,
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
function getSimplifiedSpecDir(featureName) {
|
|
630
|
-
const sddDir = existsSync(nodePath.join(process.cwd(), ".spec")) ? ".spec" : ".kiro";
|
|
631
|
-
return nodePath.join(process.cwd(), sddDir, "specs", featureName);
|
|
632
|
-
}
|
|
633
|
-
function ensureSimplifiedFeatureExists(featureName) {
|
|
634
|
-
const specDir = getSimplifiedSpecDir(featureName);
|
|
635
|
-
const specPath = nodePath.join(specDir, "spec.json");
|
|
636
|
-
if (!existsSync(specPath)) {
|
|
637
|
-
throw new Error(`Feature "${featureName}" not found. Run sdd-init first or check the feature name.`);
|
|
638
|
-
}
|
|
639
|
-
return { specDir, specPath };
|
|
640
|
-
}
|
|
641
|
-
async function generateCompactHandoffSimplified(featureName, approvedPhase) {
|
|
642
|
-
const fs = await import("fs");
|
|
643
|
-
const path = await import("path");
|
|
644
|
-
const { specDir } = ensureSimplifiedFeatureExists(featureName);
|
|
645
|
-
const contextDir = path.join(specDir, "context");
|
|
646
|
-
const documents = ["requirements.md", "design.md", "tasks.md", "spec.json"]
|
|
647
|
-
.map((name) => {
|
|
648
|
-
const filePath = path.join(specDir, name);
|
|
649
|
-
return fs.existsSync(filePath)
|
|
650
|
-
? { name, filePath, content: fs.readFileSync(filePath, "utf8") }
|
|
651
|
-
: null;
|
|
652
|
-
})
|
|
653
|
-
.filter(Boolean);
|
|
654
|
-
if (documents.length === 0) {
|
|
655
|
-
throw new Error(`Feature "${featureName}" has no source documents to compact.`);
|
|
656
|
-
}
|
|
657
|
-
fs.mkdirSync(contextDir, { recursive: true });
|
|
658
|
-
const sourceCharacters = documents.reduce((sum, doc) => sum + doc.content.length, 0);
|
|
659
|
-
const spec = documents.find((doc) => doc.name === "spec.json");
|
|
660
|
-
const parsedSpec = spec ? JSON.parse(spec.content) : {};
|
|
661
|
-
const summaries = documents.map((doc) => summarizeDocumentSimplified(doc.name, doc.content));
|
|
662
|
-
const nextActions = getSimplifiedNextActions(approvedPhase, parsedSpec);
|
|
663
|
-
const draft = [
|
|
664
|
-
`# SDD Context Handoff: ${featureName}`,
|
|
665
|
-
"",
|
|
666
|
-
`Generated: ${new Date().toISOString()}`,
|
|
667
|
-
`Approved phase: ${approvedPhase}`,
|
|
668
|
-
"",
|
|
669
|
-
"## Workflow State",
|
|
670
|
-
"",
|
|
671
|
-
`- Requirements: ${formatSimplifiedApproval(parsedSpec.approvals?.requirements)}`,
|
|
672
|
-
`- Design: ${formatSimplifiedApproval(parsedSpec.approvals?.design)}`,
|
|
673
|
-
`- Tasks: ${formatSimplifiedApproval(parsedSpec.approvals?.tasks)}`,
|
|
674
|
-
parsedSpec.checkpoints?.test_cases?.required
|
|
675
|
-
? `- TDD test-case review: ${parsedSpec.checkpoints.test_cases.reviewed ? "reviewed" : "pending"}`
|
|
676
|
-
: "- TDD test-case review: not required",
|
|
677
|
-
"",
|
|
678
|
-
"## Compact Phase Summaries",
|
|
679
|
-
"",
|
|
680
|
-
summaries.join("\n\n"),
|
|
681
|
-
"",
|
|
682
|
-
"## Next Actions",
|
|
683
|
-
"",
|
|
684
|
-
...nextActions.map((action) => `- ${action}`),
|
|
685
|
-
"",
|
|
686
|
-
"## Source References",
|
|
687
|
-
"",
|
|
688
|
-
...documents.map((doc) => `- ${doc.filePath}`),
|
|
689
|
-
"",
|
|
690
|
-
"## Context Budget Estimate",
|
|
691
|
-
"",
|
|
692
|
-
`- Full source context: ~${estimateTokensSimplified(sourceCharacters)} tokens`,
|
|
693
|
-
"- Handoff context: ~0 tokens",
|
|
694
|
-
"- Use `sdd-context-load` default compact mode for routine continuation.",
|
|
695
|
-
'- Use `sdd-context-load` with `mode: "full"` only for audits or ambiguous decisions.'
|
|
696
|
-
].join("\n");
|
|
697
|
-
const compactTokens = estimateTokensSimplified(draft.length);
|
|
698
|
-
const sourceTokens = estimateTokensSimplified(sourceCharacters);
|
|
699
|
-
const reductionPercentage = sourceTokens === 0
|
|
700
|
-
? 0
|
|
701
|
-
: Math.max(0, Math.round((1 - compactTokens / sourceTokens) * 100));
|
|
702
|
-
const content = draft.replace("- Handoff context: ~0 tokens", `- Handoff context: ~${compactTokens} tokens`);
|
|
703
|
-
const handoffPath = path.join(contextDir, "handoff.md");
|
|
704
|
-
fs.writeFileSync(handoffPath, content);
|
|
705
|
-
fs.writeFileSync(path.join(contextDir, `${approvedPhase}-handoff.md`), content);
|
|
706
|
-
return {
|
|
707
|
-
relativePath: path.relative(process.cwd(), handoffPath),
|
|
708
|
-
sourceTokens,
|
|
709
|
-
compactTokens,
|
|
710
|
-
reductionPercentage,
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
async function loadFullContextSimplified(featureName) {
|
|
714
|
-
const fs = await import("fs");
|
|
715
|
-
const path = await import("path");
|
|
716
|
-
const { specDir } = ensureSimplifiedFeatureExists(featureName);
|
|
717
|
-
const parts = [`# Full SDD Context: ${featureName}`];
|
|
718
|
-
for (const name of ["requirements.md", "design.md", "tasks.md", "spec.json"]) {
|
|
719
|
-
const filePath = path.join(specDir, name);
|
|
720
|
-
if (fs.existsSync(filePath)) {
|
|
721
|
-
parts.push(`## ${name}\n\n${fs.readFileSync(filePath, "utf8")}`);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
return parts.join("\n\n");
|
|
725
|
-
}
|
|
726
|
-
function summarizeDocumentSimplified(name, content) {
|
|
727
|
-
const lines = content
|
|
728
|
-
.split(/\r?\n/)
|
|
729
|
-
.map((line) => line.trim())
|
|
730
|
-
.filter((line) => line.length > 0 && !line.startsWith("```"));
|
|
731
|
-
const headings = lines
|
|
732
|
-
.filter((line) => /^#{1,4}\s+/.test(line))
|
|
733
|
-
.map((line) => line.replace(/^#{1,4}\s+/, "").trim())
|
|
734
|
-
.filter(uniqueSimplifiedLine)
|
|
735
|
-
.slice(0, 8);
|
|
736
|
-
const bullets = lines
|
|
737
|
-
.filter((line) => /^[-*]\s+/.test(line) || /^\d+\.\s+/.test(line))
|
|
738
|
-
.map((line) => line.replace(/^[-*]\s+/, "").replace(/^\d+\.\s+/, "").trim())
|
|
739
|
-
.filter((line) => line.length <= 220)
|
|
740
|
-
.filter(uniqueSimplifiedLine)
|
|
741
|
-
.slice(0, 8);
|
|
742
|
-
const requirementLines = lines
|
|
743
|
-
.filter((line) => /\b(SHALL|MUST|WHEN|IF|THEN|WHERE|constraint|risk|security|performance|error|edge case)\b/i.test(line))
|
|
744
|
-
.map((line) => line.replace(/^[-*]\s+/, "").replace(/^\d+\.\s+/, "").trim())
|
|
745
|
-
.filter((line) => line.length <= 240)
|
|
746
|
-
.filter(uniqueSimplifiedLine)
|
|
747
|
-
.slice(0, 8);
|
|
748
|
-
return [
|
|
749
|
-
`### ${name}`,
|
|
750
|
-
headings.length > 0 ? "\n**Key sections:**" : "",
|
|
751
|
-
...headings.map((line) => `- ${line}`),
|
|
752
|
-
bullets.length > 0 ? "\n**Important points:**" : "",
|
|
753
|
-
...bullets.map((line) => `- ${line}`),
|
|
754
|
-
requirementLines.length > 0 ? "\n**Constraints and acceptance signals:**" : "",
|
|
755
|
-
...requirementLines.map((line) => `- ${line}`),
|
|
756
|
-
headings.length === 0 && bullets.length === 0 && requirementLines.length === 0
|
|
757
|
-
? "- No structured summary points found; open source document if this phase is active."
|
|
758
|
-
: "",
|
|
759
|
-
].filter(Boolean).join("\n");
|
|
760
|
-
}
|
|
761
|
-
function uniqueSimplifiedLine(line, index, lines) {
|
|
762
|
-
return lines.findIndex((candidate) => candidate.toLowerCase() === line.toLowerCase()) === index;
|
|
763
|
-
}
|
|
764
|
-
function getSimplifiedNextActions(approvedPhase, spec) {
|
|
765
|
-
if (approvedPhase === "requirements") {
|
|
766
|
-
return ["Generate or review design using the approved requirements handoff."];
|
|
767
|
-
}
|
|
768
|
-
if (approvedPhase === "design") {
|
|
769
|
-
return ["Generate TDD task breakdown from the approved design handoff."];
|
|
770
|
-
}
|
|
771
|
-
if (spec.checkpoints?.test_cases?.required && !spec.checkpoints.test_cases.reviewed) {
|
|
772
|
-
return ["Review TDD test cases, then run sdd-review-test-cases before approving tasks."];
|
|
773
|
-
}
|
|
774
|
-
return ["Begin implementation with compact context loaded; open full docs only for ambiguous details."];
|
|
775
|
-
}
|
|
776
|
-
function formatSimplifiedApproval(status) {
|
|
777
|
-
if (status?.generated && status?.approved)
|
|
778
|
-
return "generated, approved";
|
|
779
|
-
if (status?.generated)
|
|
780
|
-
return "generated, pending approval";
|
|
781
|
-
return "not generated";
|
|
782
|
-
}
|
|
783
|
-
function estimateTokensSimplified(characters) {
|
|
784
|
-
return Math.max(1, Math.ceil(characters / 4));
|
|
28
|
+
export async function startMCPServer() {
|
|
29
|
+
const runtime = await createMCPServer();
|
|
30
|
+
await runtime.mcpServer.start();
|
|
31
|
+
return runtime;
|
|
785
32
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
const { featureName } = args || {};
|
|
790
|
-
if (!featureName) {
|
|
791
|
-
return {
|
|
792
|
-
content: [{ type: "text", text: "featureName is required" }],
|
|
793
|
-
isError: true,
|
|
794
|
-
};
|
|
795
|
-
}
|
|
796
|
-
try {
|
|
797
|
-
const sddDir = fs.existsSync(path.join(process.cwd(), ".spec")) ? ".spec" : ".kiro";
|
|
798
|
-
const specPath = path.join(process.cwd(), sddDir, "specs", featureName, "spec.json");
|
|
799
|
-
const spec = JSON.parse(fs.readFileSync(specPath, "utf8"));
|
|
800
|
-
spec.workflow_options = {
|
|
801
|
-
...(spec.workflow_options ?? {}),
|
|
802
|
-
review_test_cases: true,
|
|
803
|
-
};
|
|
804
|
-
spec.checkpoints = {
|
|
805
|
-
...(spec.checkpoints ?? {}),
|
|
806
|
-
test_cases: {
|
|
807
|
-
required: true,
|
|
808
|
-
reviewed: true,
|
|
809
|
-
reviewed_at: new Date().toISOString(),
|
|
810
|
-
},
|
|
811
|
-
};
|
|
812
|
-
spec.updated_at = new Date().toISOString();
|
|
813
|
-
spec.ready_for_implementation =
|
|
814
|
-
spec.approvals?.requirements?.approved === true &&
|
|
815
|
-
spec.approvals?.design?.approved === true &&
|
|
816
|
-
spec.approvals?.tasks?.approved === true;
|
|
817
|
-
fs.writeFileSync(specPath, JSON.stringify(spec, null, 2));
|
|
818
|
-
return {
|
|
819
|
-
content: [
|
|
820
|
-
{
|
|
821
|
-
type: "text",
|
|
822
|
-
text: `## TDD Test Cases Reviewed\n\n**Feature**: ${featureName}\n**Status**: ✅ Reviewed`,
|
|
823
|
-
},
|
|
824
|
-
],
|
|
825
|
-
};
|
|
826
|
-
}
|
|
827
|
-
catch (error) {
|
|
828
|
-
return {
|
|
829
|
-
content: [
|
|
830
|
-
{
|
|
831
|
-
type: "text",
|
|
832
|
-
text: `Error reviewing test cases: ${error.message}`,
|
|
833
|
-
},
|
|
834
|
-
],
|
|
835
|
-
isError: true,
|
|
836
|
-
};
|
|
837
|
-
}
|
|
33
|
+
function isDirectExecution() {
|
|
34
|
+
return process.argv[1]?.endsWith('/dist/index.js') === true ||
|
|
35
|
+
process.argv[1]?.endsWith('\\dist\\index.js') === true;
|
|
838
36
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
const issues = [];
|
|
845
|
-
if (code.includes("console.log"))
|
|
846
|
-
issues.push("L1: Remove debug console.log statements");
|
|
847
|
-
if (code.includes("var "))
|
|
848
|
-
issues.push("L1: Use let/const instead of var");
|
|
849
|
-
if (!/^[a-z]/.test(code.split("function ")[1]?.split("(")[0] || "")) {
|
|
850
|
-
if (code.includes("function "))
|
|
851
|
-
issues.push("L2: Function names should start with lowercase");
|
|
852
|
-
}
|
|
853
|
-
if (code.includes("for") && !code.includes("const"))
|
|
854
|
-
issues.push("L3: Prefer const in for loops");
|
|
855
|
-
if (lines.length > 50)
|
|
856
|
-
issues.push("L4: Consider splitting large blocks into smaller functions");
|
|
857
|
-
const report = `## Code Quality Analysis (${language})\n\n` +
|
|
858
|
-
(issues.length
|
|
859
|
-
? issues.map((i) => `- ${i}`).join("\n")
|
|
860
|
-
: "No significant issues detected");
|
|
861
|
-
return { content: [{ type: "text", text: report }] };
|
|
862
|
-
}
|
|
863
|
-
catch (error) {
|
|
864
|
-
return {
|
|
865
|
-
content: [
|
|
866
|
-
{
|
|
867
|
-
type: "text",
|
|
868
|
-
text: `Error analyzing code quality: ${error.message}`,
|
|
869
|
-
},
|
|
870
|
-
],
|
|
871
|
-
isError: true,
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
function generateFeatureName(description) {
|
|
876
|
-
// Extract feature name from description - similar to kiro spec-init
|
|
877
|
-
const cleaned = description
|
|
878
|
-
.toLowerCase()
|
|
879
|
-
.replace(/[^a-z0-9\s]/g, "")
|
|
880
|
-
.trim()
|
|
881
|
-
.split(/\s+/)
|
|
882
|
-
.slice(0, 4) // Take first 4 words
|
|
883
|
-
.join("-");
|
|
884
|
-
// Ensure it's not empty
|
|
885
|
-
return cleaned || "new-feature";
|
|
886
|
-
}
|
|
887
|
-
async function ensureUniqueFeatureName(baseName) {
|
|
888
|
-
const fs = await import("fs");
|
|
889
|
-
const path = await import("path");
|
|
890
|
-
// Support both .spec (new) and .kiro (legacy)
|
|
891
|
-
const sddDir = fs.existsSync(path.join(process.cwd(), ".spec")) ? ".spec" : ".kiro";
|
|
892
|
-
const specsDir = path.join(process.cwd(), sddDir, "specs");
|
|
893
|
-
if (!fs.existsSync(specsDir)) {
|
|
894
|
-
return baseName;
|
|
895
|
-
}
|
|
896
|
-
let counter = 1;
|
|
897
|
-
let featureName = baseName;
|
|
898
|
-
while (fs.existsSync(path.join(specsDir, featureName))) {
|
|
899
|
-
featureName = `${baseName}-${counter}`;
|
|
900
|
-
counter++;
|
|
901
|
-
}
|
|
902
|
-
return featureName;
|
|
903
|
-
}
|
|
904
|
-
async function loadSpecContext(featureName) {
|
|
905
|
-
const fs = await import("fs");
|
|
906
|
-
const path = await import("path");
|
|
907
|
-
// Support both .spec (new) and .kiro (legacy)
|
|
908
|
-
const sddDir = fs.existsSync(path.join(process.cwd(), ".spec")) ? ".spec" : ".kiro";
|
|
909
|
-
const specDir = path.join(process.cwd(), sddDir, "specs", featureName);
|
|
910
|
-
const specJsonPath = path.join(specDir, "spec.json");
|
|
911
|
-
const requirementsPath = path.join(specDir, "requirements.md");
|
|
912
|
-
let spec = null;
|
|
913
|
-
let requirements = null;
|
|
914
|
-
try {
|
|
915
|
-
if (fs.existsSync(specJsonPath)) {
|
|
916
|
-
const specContent = fs.readFileSync(specJsonPath, "utf8");
|
|
917
|
-
spec = JSON.parse(specContent);
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
catch (error) {
|
|
921
|
-
// Ignore spec.json parse errors
|
|
922
|
-
}
|
|
923
|
-
try {
|
|
924
|
-
if (fs.existsSync(requirementsPath)) {
|
|
925
|
-
requirements = fs.readFileSync(requirementsPath, "utf8");
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
catch (error) {
|
|
929
|
-
// Ignore requirements.md read errors
|
|
930
|
-
}
|
|
931
|
-
return { spec, requirements };
|
|
932
|
-
}
|
|
933
|
-
async function handleInitSimplified(args) {
|
|
934
|
-
const fs = await import("fs");
|
|
935
|
-
const path = await import("path");
|
|
936
|
-
try {
|
|
937
|
-
const { projectName, description, reviewTestCases = false } = args;
|
|
938
|
-
if (!description || typeof description !== "string") {
|
|
939
|
-
throw new Error("Description is required for project initialization");
|
|
940
|
-
}
|
|
941
|
-
const projectPath = process.cwd();
|
|
942
|
-
// Generate feature name from projectName (if provided) or description
|
|
943
|
-
const baseFeatureName = projectName && typeof projectName === "string"
|
|
944
|
-
? projectName
|
|
945
|
-
: generateFeatureName(description);
|
|
946
|
-
const featureName = await ensureUniqueFeatureName(baseFeatureName);
|
|
947
|
-
// Create .spec/specs/[feature-name] directory (use .spec for new projects)
|
|
948
|
-
const specDir = path.join(projectPath, ".spec", "specs", featureName);
|
|
949
|
-
if (!fs.existsSync(specDir)) {
|
|
950
|
-
fs.mkdirSync(specDir, { recursive: true });
|
|
951
|
-
}
|
|
952
|
-
// Create spec.json with metadata
|
|
953
|
-
const specContent = {
|
|
954
|
-
feature_name: featureName,
|
|
955
|
-
created_at: new Date().toISOString(),
|
|
956
|
-
updated_at: new Date().toISOString(),
|
|
957
|
-
language: "en",
|
|
958
|
-
phase: "initialized",
|
|
959
|
-
approvals: {
|
|
960
|
-
requirements: {
|
|
961
|
-
generated: false,
|
|
962
|
-
approved: false,
|
|
963
|
-
},
|
|
964
|
-
design: {
|
|
965
|
-
generated: false,
|
|
966
|
-
approved: false,
|
|
967
|
-
},
|
|
968
|
-
tasks: {
|
|
969
|
-
generated: false,
|
|
970
|
-
approved: false,
|
|
971
|
-
},
|
|
972
|
-
},
|
|
973
|
-
workflow_options: {
|
|
974
|
-
review_test_cases: reviewTestCases === true,
|
|
975
|
-
},
|
|
976
|
-
checkpoints: {
|
|
977
|
-
test_cases: {
|
|
978
|
-
required: reviewTestCases === true,
|
|
979
|
-
reviewed: reviewTestCases !== true,
|
|
980
|
-
},
|
|
981
|
-
},
|
|
982
|
-
ready_for_implementation: false,
|
|
983
|
-
};
|
|
984
|
-
fs.writeFileSync(path.join(specDir, "spec.json"), JSON.stringify(specContent, null, 2));
|
|
985
|
-
// Create requirements.md template with project description
|
|
986
|
-
const requirementsTemplate = `# Requirements Document
|
|
987
|
-
|
|
988
|
-
## Project Description (Input)
|
|
989
|
-
${description}
|
|
990
|
-
|
|
991
|
-
## Requirements
|
|
992
|
-
<!-- Will be generated in sdd-requirements phase -->
|
|
993
|
-
`;
|
|
994
|
-
fs.writeFileSync(path.join(specDir, "requirements.md"), requirementsTemplate);
|
|
995
|
-
// Ensure AGENTS.md exists (static, derived from CLAUDE.md when available)
|
|
996
|
-
const agentsPath = path.join(process.cwd(), "AGENTS.md");
|
|
997
|
-
if (!fs.existsSync(agentsPath)) {
|
|
998
|
-
const claudePath = path.join(process.cwd(), "CLAUDE.md");
|
|
999
|
-
let agentsContent = "";
|
|
1000
|
-
if (fs.existsSync(claudePath)) {
|
|
1001
|
-
const claudeContent = fs.readFileSync(claudePath, "utf8");
|
|
1002
|
-
agentsContent = claudeContent
|
|
1003
|
-
.replace(/# Claude Code Spec-Driven Development/g, "# AI Agent Spec-Driven Development")
|
|
1004
|
-
.replace(/Claude Code/g, "AI Agent")
|
|
1005
|
-
.replace(/claude code/g, "ai agent")
|
|
1006
|
-
.replace(/Claude/g, "AI Agent")
|
|
1007
|
-
.replace(/claude/g, "ai agent");
|
|
1008
|
-
}
|
|
1009
|
-
else {
|
|
1010
|
-
agentsContent = `# AI Agent Spec-Driven Development
|
|
1011
|
-
|
|
1012
|
-
Spec Driven Development implementation using MCP tools.
|
|
1013
|
-
|
|
1014
|
-
## Project Context
|
|
1015
|
-
|
|
1016
|
-
### Paths
|
|
1017
|
-
- Steering: \`.spec/steering/\`
|
|
1018
|
-
- Specs: \`.spec/specs/\`
|
|
1019
|
-
- Skills: install with \`npx sdd-mcp-server install --target codex --skills\` or \`--target claude-code\`
|
|
1020
|
-
|
|
1021
|
-
### Steering vs Specification
|
|
1022
|
-
|
|
1023
|
-
**Steering** (\`.spec/steering/\`) - Guide AI with project-wide rules and context
|
|
1024
|
-
**Specs** (\`.spec/specs/\`) - Formalize development process for individual features
|
|
1025
|
-
|
|
1026
|
-
### Active Specifications
|
|
1027
|
-
- Check \`.spec/specs/\` for active specifications
|
|
1028
|
-
- Use \`sdd-status\` to check progress
|
|
1029
|
-
|
|
1030
|
-
**Current Specifications:**
|
|
1031
|
-
- (None active)
|
|
1032
|
-
|
|
1033
|
-
## Development Guidelines
|
|
1034
|
-
- Think in English, generate responses in English
|
|
1035
|
-
|
|
1036
|
-
## Workflow
|
|
1037
|
-
|
|
1038
|
-
### Phase 0: Steering (Optional)
|
|
1039
|
-
\`sdd-steering\` - Create/update steering documents
|
|
1040
|
-
\`sdd-steering-custom\` - Create custom steering for specialized contexts
|
|
1041
|
-
|
|
1042
|
-
Note: Optional for new features or small additions. You can proceed directly to sdd-init.
|
|
1043
|
-
|
|
1044
|
-
### Phase 1: Specification Creation
|
|
1045
|
-
1. \`sdd-init\` - Initialize spec with detailed project description
|
|
1046
|
-
2. \`sdd-requirements\` - Generate requirements document
|
|
1047
|
-
3. \`sdd-design\` - Interactive: "Have you reviewed requirements.md? [y/N]"
|
|
1048
|
-
4. \`sdd-tasks\` - Interactive: Confirms requirements/design review and asks whether TDD test cases need a review checkpoint
|
|
1049
|
-
|
|
1050
|
-
### Phase 2: Progress Tracking
|
|
1051
|
-
\`sdd-status\` - Check current progress and phases
|
|
1052
|
-
|
|
1053
|
-
## Development Rules
|
|
1054
|
-
1. **Consider steering**: Run \`sdd-steering\` before major development (optional for new features)
|
|
1055
|
-
2. **Follow 3-phase approval workflow**: Requirements → Design → Tasks → Implementation
|
|
1056
|
-
3. **Approval required**: Each phase requires human review (interactive prompt or manual)
|
|
1057
|
-
4. **No skipping phases**: Design requires approved requirements; Tasks require approved design
|
|
1058
|
-
5. **Update task status**: Mark tasks as completed when working on them
|
|
1059
|
-
6. **Keep steering current**: Run \`sdd-steering\` after significant changes
|
|
1060
|
-
7. **Check spec compliance**: Use \`sdd-status\` to verify alignment
|
|
1061
|
-
|
|
1062
|
-
## Steering Configuration
|
|
1063
|
-
|
|
1064
|
-
### Current Steering Files
|
|
1065
|
-
Managed by \`sdd-steering\` tool. Updates here reflect tool changes.
|
|
1066
|
-
|
|
1067
|
-
### Active Steering Files
|
|
1068
|
-
Load steering documents on demand to control token usage:
|
|
1069
|
-
- \`product.md\`: Product context and business objectives
|
|
1070
|
-
- \`tech.md\`: Technology stack and architectural decisions
|
|
1071
|
-
- \`structure.md\`: File organization and code patterns
|
|
1072
|
-
- \`linus-review.md\`: Code quality review standards
|
|
1073
|
-
- \`commit.md\`: Commit and PR conventions
|
|
1074
|
-
- \`security-check.md\`: OWASP Top 10 checklist for code generation and review
|
|
1075
|
-
- \`tdd-guideline.md\`: TDD workflow for new features
|
|
1076
|
-
- \`principles.md\`: SOLID, DRY, KISS, YAGNI, Separation of Concerns, Modularity
|
|
1077
|
-
|
|
1078
|
-
### Custom Steering Files
|
|
1079
|
-
<!-- Added by sdd-steering-custom tool -->
|
|
1080
|
-
<!-- Format:
|
|
1081
|
-
- \`filename.md\`: Mode - Pattern(s) - Description
|
|
1082
|
-
Mode: Always|Conditional|Manual
|
|
1083
|
-
Pattern: File patterns for Conditional mode
|
|
1084
|
-
-->
|
|
1085
|
-
|
|
1086
|
-
### Inclusion Modes
|
|
1087
|
-
- **Always**: Loaded in every interaction (default)
|
|
1088
|
-
- **Conditional**: Loaded for specific file patterns (e.g., "*.test.js")
|
|
1089
|
-
- **Manual**: Reference with \`@filename.md\` syntax
|
|
1090
|
-
`;
|
|
1091
|
-
}
|
|
1092
|
-
fs.writeFileSync(agentsPath, agentsContent);
|
|
1093
|
-
}
|
|
1094
|
-
return {
|
|
1095
|
-
content: [
|
|
1096
|
-
{
|
|
1097
|
-
type: "text",
|
|
1098
|
-
text: `## SDD Project Initialized Successfully
|
|
1099
|
-
|
|
1100
|
-
**Feature Name**: \`${featureName}\`
|
|
1101
|
-
**Description**: ${description}
|
|
1102
|
-
**TDD Test Case Review Checkpoint**: ${reviewTestCases === true ? "Enabled" : "Disabled"}
|
|
1103
|
-
|
|
1104
|
-
**Created Files**:
|
|
1105
|
-
- \`.spec/specs/${featureName}/spec.json\` - Project metadata and phase tracking
|
|
1106
|
-
- \`.spec/specs/${featureName}/requirements.md\` - Initial requirements template
|
|
1107
|
-
|
|
1108
|
-
**Next Steps**:
|
|
1109
|
-
1. Run \`sdd-requirements ${featureName}\` to generate detailed requirements
|
|
1110
|
-
2. Follow the SDD workflow: Requirements → Design → Tasks → Implementation
|
|
1111
|
-
|
|
1112
|
-
**Workflow Phase**: Initialized
|
|
1113
|
-
**Ready for**: Requirements generation`,
|
|
1114
|
-
},
|
|
1115
|
-
],
|
|
1116
|
-
};
|
|
1117
|
-
}
|
|
1118
|
-
catch (error) {
|
|
1119
|
-
return {
|
|
1120
|
-
content: [
|
|
1121
|
-
{
|
|
1122
|
-
type: "text",
|
|
1123
|
-
text: `Error initializing SDD project: ${error.message}`,
|
|
1124
|
-
},
|
|
1125
|
-
],
|
|
1126
|
-
isError: true,
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
async function main() {
|
|
1131
|
-
try {
|
|
1132
|
-
if (isMCPMode) {
|
|
1133
|
-
// Use simplified MCP server for MCP mode
|
|
1134
|
-
await createSimpleMCPServer();
|
|
1135
|
-
}
|
|
1136
|
-
else {
|
|
1137
|
-
// Use full featured server for development/testing mode
|
|
1138
|
-
const server = await createMCPServer();
|
|
1139
|
-
const { logger, mcpServer, pluginManager, hookSystem, toolRegistry, steeringRegistry, } = server;
|
|
1140
|
-
logger.info("MCP SDD Server starting...", {
|
|
1141
|
-
version: process.env.npm_package_version ?? "1.0.0",
|
|
1142
|
-
nodeVersion: process.version,
|
|
1143
|
-
pid: process.pid,
|
|
1144
|
-
});
|
|
1145
|
-
await mcpServer.start();
|
|
1146
|
-
// Get plugin system statistics
|
|
1147
|
-
const pluginStats = await pluginManager.getAllPlugins();
|
|
1148
|
-
const hookStats = await hookSystem.getAllHooks();
|
|
1149
|
-
const toolStats = await toolRegistry.getAllTools();
|
|
1150
|
-
const steeringStats = await steeringRegistry.getSteeringStatistics();
|
|
1151
|
-
logger.info("MCP SDD Server ready for connections", {
|
|
1152
|
-
capabilities: {
|
|
1153
|
-
workflow: "5-phase SDD workflow state machine (INIT→REQUIREMENTS→DESIGN→TASKS→IMPLEMENTATION)",
|
|
1154
|
-
validation: "Cross-phase validation with approval gates and rollback support",
|
|
1155
|
-
initialization: "Project setup with .spec directory structure and spec.json",
|
|
1156
|
-
context: "Project memory with codebase analysis and context persistence",
|
|
1157
|
-
steering: "Dynamic steering document management with Always/Conditional/Manual modes",
|
|
1158
|
-
quality: "Linus-style code review with 5-layer analysis framework",
|
|
1159
|
-
i18n: "10-language support with cultural adaptation",
|
|
1160
|
-
plugins: `${pluginStats.length} plugins loaded with extensibility framework`,
|
|
1161
|
-
templates: "Handlebars-based template generation with inheritance",
|
|
1162
|
-
},
|
|
1163
|
-
tools: {
|
|
1164
|
-
count: 11,
|
|
1165
|
-
categories: [
|
|
1166
|
-
"sdd-init",
|
|
1167
|
-
"sdd-requirements",
|
|
1168
|
-
"sdd-design",
|
|
1169
|
-
"sdd-tasks",
|
|
1170
|
-
"sdd-implement",
|
|
1171
|
-
"sdd-status",
|
|
1172
|
-
"sdd-approve",
|
|
1173
|
-
"sdd-review-test-cases",
|
|
1174
|
-
"sdd-quality-check",
|
|
1175
|
-
"sdd-context-load",
|
|
1176
|
-
"sdd-template-render",
|
|
1177
|
-
],
|
|
1178
|
-
pluginTools: Object.keys(toolStats).length,
|
|
1179
|
-
},
|
|
1180
|
-
hooks: {
|
|
1181
|
-
registered: Object.keys(hookStats).length,
|
|
1182
|
-
phases: [
|
|
1183
|
-
"PRE_INIT",
|
|
1184
|
-
"POST_INIT",
|
|
1185
|
-
"PRE_REQUIREMENTS",
|
|
1186
|
-
"POST_REQUIREMENTS",
|
|
1187
|
-
"PRE_DESIGN",
|
|
1188
|
-
"POST_DESIGN",
|
|
1189
|
-
"PRE_TASKS",
|
|
1190
|
-
"POST_TASKS",
|
|
1191
|
-
"PRE_IMPLEMENTATION",
|
|
1192
|
-
"POST_IMPLEMENTATION",
|
|
1193
|
-
],
|
|
1194
|
-
},
|
|
1195
|
-
steering: {
|
|
1196
|
-
documents: steeringStats.totalDocuments,
|
|
1197
|
-
plugins: Object.keys(steeringStats.documentsByPlugin).length,
|
|
1198
|
-
modes: steeringStats.documentsByMode,
|
|
1199
|
-
},
|
|
1200
|
-
});
|
|
1201
|
-
// Handle graceful shutdown
|
|
1202
|
-
process.on("SIGINT", async () => {
|
|
1203
|
-
logger.info("Received SIGINT, shutting down gracefully...");
|
|
1204
|
-
await mcpServer.stop();
|
|
1205
|
-
process.exit(0);
|
|
1206
|
-
});
|
|
1207
|
-
process.on("SIGTERM", async () => {
|
|
1208
|
-
logger.info("Received SIGTERM, shutting down gracefully...");
|
|
1209
|
-
await mcpServer.stop();
|
|
1210
|
-
process.exit(0);
|
|
1211
|
-
});
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
catch (error) {
|
|
1215
|
-
// Only log startup errors in non-MCP mode
|
|
1216
|
-
if (!isMCPMode) {
|
|
1217
|
-
console.error("Failed to start MCP SDD Server:", error);
|
|
1218
|
-
}
|
|
1219
|
-
process.exit(1);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
// Only run main if this file is executed directly
|
|
1223
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
1224
|
-
void main();
|
|
37
|
+
if (isDirectExecution()) {
|
|
38
|
+
startMCPServer().catch((error) => {
|
|
39
|
+
process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
40
|
+
process.exitCode = 1;
|
|
41
|
+
});
|
|
1225
42
|
}
|
|
1226
43
|
//# sourceMappingURL=index.js.map
|