bobs-workshop 0.1.4
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/LICENSE +21 -0
- package/README.md +252 -0
- package/bin/bobs-mcp.js +130 -0
- package/dist/api/taskLogger.js +106 -0
- package/dist/api/taskLogger.js.map +1 -0
- package/dist/cli/checker.js +401 -0
- package/dist/cli/checker.js.map +1 -0
- package/dist/cli/cleanup.js +131 -0
- package/dist/cli/cleanup.js.map +1 -0
- package/dist/cli/debug.js +157 -0
- package/dist/cli/debug.js.map +1 -0
- package/dist/cli/health.js +97 -0
- package/dist/cli/health.js.map +1 -0
- package/dist/cli/setup.js +81 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/workshop.js +42 -0
- package/dist/cli/workshop.js.map +1 -0
- package/dist/dashboard/server.js +1206 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/index.js +757 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/architect.js +157 -0
- package/dist/prompts/architect.js.map +1 -0
- package/dist/prompts/debugger.js +201 -0
- package/dist/prompts/debugger.js.map +1 -0
- package/dist/prompts/engineer.js +171 -0
- package/dist/prompts/engineer.js.map +1 -0
- package/dist/prompts/orchestrator.js +225 -0
- package/dist/prompts/orchestrator.js.map +1 -0
- package/dist/prompts/reviewer.js +199 -0
- package/dist/prompts/reviewer.js.map +1 -0
- package/dist/services/activitySummarizer.js +353 -0
- package/dist/services/activitySummarizer.js.map +1 -0
- package/dist/services/changeValidator.js +396 -0
- package/dist/services/changeValidator.js.map +1 -0
- package/dist/services/claudeOrchestrator.js +343 -0
- package/dist/services/claudeOrchestrator.js.map +1 -0
- package/dist/services/fileMonitor.js +250 -0
- package/dist/services/fileMonitor.js.map +1 -0
- package/dist/services/implementationSummarizer.js +306 -0
- package/dist/services/implementationSummarizer.js.map +1 -0
- package/dist/services/liveMonitor.js +315 -0
- package/dist/services/liveMonitor.js.map +1 -0
- package/dist/services/mcpAuditLogger.js +104 -0
- package/dist/services/mcpAuditLogger.js.map +1 -0
- package/dist/services/mcpLogger.js +223 -0
- package/dist/services/mcpLogger.js.map +1 -0
- package/dist/services/tmuxManager.js +541 -0
- package/dist/services/tmuxManager.js.map +1 -0
- package/dist/tools/approvalTools.js +244 -0
- package/dist/tools/approvalTools.js.map +1 -0
- package/dist/tools/autoDebugger.js +147 -0
- package/dist/tools/autoDebugger.js.map +1 -0
- package/dist/tools/cleanupService.js +221 -0
- package/dist/tools/cleanupService.js.map +1 -0
- package/dist/tools/dashboardTools.js +359 -0
- package/dist/tools/dashboardTools.js.map +1 -0
- package/dist/tools/developmentNudges.js +336 -0
- package/dist/tools/developmentNudges.js.map +1 -0
- package/dist/tools/gitTools.js +741 -0
- package/dist/tools/gitTools.js.map +1 -0
- package/dist/tools/orchestratorTools.js +765 -0
- package/dist/tools/orchestratorTools.js.map +1 -0
- package/dist/tools/searchTools.js +788 -0
- package/dist/tools/searchTools.js.map +1 -0
- package/dist/tools/specTools.js +350 -0
- package/dist/tools/specTools.js.map +1 -0
- package/dist/tools/tmuxTools.js +100 -0
- package/dist/tools/tmuxTools.js.map +1 -0
- package/dist/tools/workRecorder.js +215 -0
- package/dist/tools/workRecorder.js.map +1 -0
- package/dist/tools/worktreeTools.js +705 -0
- package/dist/tools/worktreeTools.js.map +1 -0
- package/dist/utils/__tests__/integration.test.js +57 -0
- package/dist/utils/__tests__/integration.test.js.map +1 -0
- package/dist/utils/__tests__/serverDetection.test.js +151 -0
- package/dist/utils/__tests__/serverDetection.test.js.map +1 -0
- package/dist/utils/errorHandling.js +336 -0
- package/dist/utils/errorHandling.js.map +1 -0
- package/dist/utils/processManager.js +172 -0
- package/dist/utils/processManager.js.map +1 -0
- package/dist/utils/reliability.js +263 -0
- package/dist/utils/reliability.js.map +1 -0
- package/dist/utils/responseFormatter.js +250 -0
- package/dist/utils/responseFormatter.js.map +1 -0
- package/dist/utils/serverDetection.js +133 -0
- package/dist/utils/serverDetection.js.map +1 -0
- package/dist/utils/specMigration.js +105 -0
- package/dist/utils/specMigration.js.map +1 -0
- package/dist/validation/schemas.js +299 -0
- package/dist/validation/schemas.js.map +1 -0
- package/package.json +79 -0
- package/scripts/init-workspace.js +63 -0
- package/scripts/install-search-tools.js +116 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,757 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
// Import all tool handlers
|
|
6
|
+
import { specCreateHandler, specUpdateHandler, specGetHandler, specListHandler } from "./tools/specTools.js";
|
|
7
|
+
import { worktreeCreateHandler } from "./tools/worktreeTools.js";
|
|
8
|
+
import { stopDevServer } from "./utils/processManager.js";
|
|
9
|
+
import { hybridSearchHandler } from "./tools/searchTools.js";
|
|
10
|
+
import { dashboardLaunchHandler, dashboardUpdateHandler } from "./tools/dashboardTools.js";
|
|
11
|
+
import { workshopHandler } from "./tools/orchestratorTools.js";
|
|
12
|
+
// REMOVED: Approval tools imports - use structured outputs instead
|
|
13
|
+
import { getGitStatus, autoCommitChanges, autoMergeAndCleanup, findWorktreeForSpec } from "./tools/gitTools.js";
|
|
14
|
+
import { autoDebugError, shouldInvokeDebugger } from "./tools/autoDebugger.js";
|
|
15
|
+
import { cleanupService } from "./tools/cleanupService.js";
|
|
16
|
+
import { changeValidator } from "./services/changeValidator.js";
|
|
17
|
+
import { implementationSummarizer } from "./services/implementationSummarizer.js";
|
|
18
|
+
import { mcpLogger } from "./services/mcpLogger.js";
|
|
19
|
+
import { formatConciseResponse } from "./utils/responseFormatter.js";
|
|
20
|
+
import { withTimeout, createCircuitBreaker, globalHealthChecker } from "./utils/reliability.js";
|
|
21
|
+
import { withErrorHandling, globalErrorCollector } from "./utils/errorHandling.js";
|
|
22
|
+
// Import all prompts
|
|
23
|
+
import { ARCHITECT_PROMPT } from "./prompts/architect.js";
|
|
24
|
+
import { ENGINEER_PROMPT } from "./prompts/engineer.js";
|
|
25
|
+
import { DEBUGGER_PROMPT } from "./prompts/debugger.js";
|
|
26
|
+
import { REVIEWER_PROMPT } from "./prompts/reviewer.js";
|
|
27
|
+
// Global circuit breakers for external operations
|
|
28
|
+
const fileSystemCircuitBreaker = createCircuitBreaker({
|
|
29
|
+
threshold: 5,
|
|
30
|
+
timeout: 15000,
|
|
31
|
+
resetTimeout: 30000
|
|
32
|
+
});
|
|
33
|
+
// Helper function to determine appropriate timeout for different tools
|
|
34
|
+
function getToolTimeout(toolName) {
|
|
35
|
+
// Longer timeouts for complex operations
|
|
36
|
+
if (toolName.includes('search') || toolName.includes('hybrid')) {
|
|
37
|
+
return 45000; // 45 seconds for search operations
|
|
38
|
+
}
|
|
39
|
+
if (toolName.includes('workflow') || toolName.includes('create')) {
|
|
40
|
+
return 60000; // 60 seconds for workflow operations
|
|
41
|
+
}
|
|
42
|
+
return 20000; // 20 seconds default
|
|
43
|
+
}
|
|
44
|
+
// Initialize health checks for system components
|
|
45
|
+
async function initializeHealthChecks() {
|
|
46
|
+
// MCP server health check
|
|
47
|
+
globalHealthChecker.register('mcp_server', async () => {
|
|
48
|
+
try {
|
|
49
|
+
// Basic functionality check
|
|
50
|
+
return process.uptime() > 0;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
// File system health check
|
|
57
|
+
globalHealthChecker.register('file_system', async () => {
|
|
58
|
+
try {
|
|
59
|
+
const fs = await import('fs-extra');
|
|
60
|
+
const path = await import('path');
|
|
61
|
+
const testPath = path.resolve(process.cwd(), '.bob');
|
|
62
|
+
await fs.ensureDir(testPath);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// Dashboard service check
|
|
70
|
+
globalHealthChecker.register('dashboard', async () => {
|
|
71
|
+
try {
|
|
72
|
+
// Check if we can create the dashboard server
|
|
73
|
+
const { createDashboardServer } = await import('./dashboard/server.js');
|
|
74
|
+
const app = createDashboardServer();
|
|
75
|
+
return app !== null;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
console.log("Health checks initialized");
|
|
82
|
+
}
|
|
83
|
+
// Graceful shutdown handler
|
|
84
|
+
async function gracefulShutdown(server) {
|
|
85
|
+
console.log("Initiating graceful shutdown...");
|
|
86
|
+
try {
|
|
87
|
+
// Stop auto-cleanup service
|
|
88
|
+
const { cleanupService } = await import('./tools/cleanupService.js');
|
|
89
|
+
cleanupService.stopAutoCleanup();
|
|
90
|
+
console.log("Auto-cleanup service stopped");
|
|
91
|
+
// Perform final cleanup
|
|
92
|
+
await cleanupService.performCleanup({
|
|
93
|
+
maxWorktreeAge: 7,
|
|
94
|
+
cleanTempFiles: true,
|
|
95
|
+
stopOrphanedRecorders: true,
|
|
96
|
+
forceCleanup: false
|
|
97
|
+
});
|
|
98
|
+
console.log("Final cleanup completed");
|
|
99
|
+
// Log shutdown statistics
|
|
100
|
+
const errorStats = globalErrorCollector.getStats();
|
|
101
|
+
console.log("Error statistics:", errorStats);
|
|
102
|
+
console.log("Graceful shutdown completed");
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error("Error during shutdown:", error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Enhanced CLI output formatting for modern terminal experience
|
|
109
|
+
function generateToolStatusMessage(toolName, args, status, duration, result, error) {
|
|
110
|
+
const toolCategory = getToolCategory(toolName);
|
|
111
|
+
const categoryIcon = getCategoryIcon(toolCategory);
|
|
112
|
+
const statusIcon = getStatusIcon(status);
|
|
113
|
+
const toolDisplayName = getToolDisplayName(toolName);
|
|
114
|
+
switch (status) {
|
|
115
|
+
case 'started':
|
|
116
|
+
return `${categoryIcon} [${toolCategory.toUpperCase()}] ${toolDisplayName} → ⏳ Starting...`;
|
|
117
|
+
case 'completed':
|
|
118
|
+
const durationText = formatDuration(duration || 0);
|
|
119
|
+
const summary = extractResultSummary(toolName, result);
|
|
120
|
+
return `${categoryIcon} [${toolCategory.toUpperCase()}] ${toolDisplayName} → ${statusIcon} ${summary} (${durationText})`;
|
|
121
|
+
case 'error':
|
|
122
|
+
const errorDuration = formatDuration(duration || 0);
|
|
123
|
+
return `${categoryIcon} [${toolCategory.toUpperCase()}] ${toolDisplayName} → ❌ Failed: ${error} (${errorDuration})`;
|
|
124
|
+
default:
|
|
125
|
+
return `${categoryIcon} [${toolCategory.toUpperCase()}] ${toolDisplayName}`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function getToolCategory(toolName) {
|
|
129
|
+
if (toolName.includes('manual'))
|
|
130
|
+
return 'manual';
|
|
131
|
+
if (toolName.includes('search'))
|
|
132
|
+
return 'search';
|
|
133
|
+
if (toolName.includes('workflow'))
|
|
134
|
+
return 'workflow';
|
|
135
|
+
if (toolName.includes('dashboard'))
|
|
136
|
+
return 'dashboard';
|
|
137
|
+
if (toolName.includes('worktree'))
|
|
138
|
+
return 'worktree';
|
|
139
|
+
if (toolName.includes('validate'))
|
|
140
|
+
return 'validation';
|
|
141
|
+
if (toolName.includes('summarize'))
|
|
142
|
+
return 'analysis';
|
|
143
|
+
if (toolName.includes('health'))
|
|
144
|
+
return 'system';
|
|
145
|
+
if (toolName.includes('cleanup'))
|
|
146
|
+
return 'maintenance';
|
|
147
|
+
if (toolName.includes('workshop'))
|
|
148
|
+
return 'orchestration';
|
|
149
|
+
return 'tool';
|
|
150
|
+
}
|
|
151
|
+
function getCategoryIcon(category) {
|
|
152
|
+
const icons = {
|
|
153
|
+
'manual': '📋',
|
|
154
|
+
'search': '🔍',
|
|
155
|
+
'workflow': '🔄',
|
|
156
|
+
'dashboard': '💻',
|
|
157
|
+
'worktree': '🌳',
|
|
158
|
+
'validation': '✅',
|
|
159
|
+
'analysis': '📊',
|
|
160
|
+
'system': '🛠️',
|
|
161
|
+
'maintenance': '🧹',
|
|
162
|
+
'session': '📺',
|
|
163
|
+
'orchestration': '🎯',
|
|
164
|
+
'tool': '⚙️'
|
|
165
|
+
};
|
|
166
|
+
return icons[category] || '⚙️';
|
|
167
|
+
}
|
|
168
|
+
function getStatusIcon(status) {
|
|
169
|
+
const icons = {
|
|
170
|
+
'completed': '✅',
|
|
171
|
+
'error': '❌',
|
|
172
|
+
'started': '⏳',
|
|
173
|
+
'success': '✅'
|
|
174
|
+
};
|
|
175
|
+
return icons[status] || '📄';
|
|
176
|
+
}
|
|
177
|
+
function getToolDisplayName(toolName) {
|
|
178
|
+
return toolName.replace('bob.', '').replace(/\./g, '.');
|
|
179
|
+
}
|
|
180
|
+
function formatDuration(ms) {
|
|
181
|
+
if (ms < 1000)
|
|
182
|
+
return `${ms}ms`;
|
|
183
|
+
if (ms < 60000)
|
|
184
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
185
|
+
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
|
|
186
|
+
}
|
|
187
|
+
function extractResultSummary(toolName, result) {
|
|
188
|
+
if (!result)
|
|
189
|
+
return 'Completed';
|
|
190
|
+
// Custom summaries based on tool type
|
|
191
|
+
if (toolName.includes('manual')) {
|
|
192
|
+
if (result.spec_id) {
|
|
193
|
+
const action = toolName.includes('create') ? 'Created' : 'Updated';
|
|
194
|
+
return `${action} ${result.spec_id.split('-').pop() || 'manual'}`;
|
|
195
|
+
}
|
|
196
|
+
return 'Manual operation completed';
|
|
197
|
+
}
|
|
198
|
+
if (toolName.includes('search')) {
|
|
199
|
+
if (result.stats?.totalResults !== undefined) {
|
|
200
|
+
const count = result.stats.totalResults;
|
|
201
|
+
return `${count} result${count !== 1 ? 's' : ''} found`;
|
|
202
|
+
}
|
|
203
|
+
if (result.lexicalHits && result.semanticHits) {
|
|
204
|
+
const total = result.lexicalHits.length + result.semanticHits.length;
|
|
205
|
+
return `${total} result${total !== 1 ? 's' : ''} found`;
|
|
206
|
+
}
|
|
207
|
+
return 'Search completed';
|
|
208
|
+
}
|
|
209
|
+
if (toolName.includes('workflow')) {
|
|
210
|
+
if (toolName.includes('start') && result.manual?.spec_id) {
|
|
211
|
+
return `Workflow started for ${result.manual.spec_id.split('-').pop()}`;
|
|
212
|
+
}
|
|
213
|
+
if (toolName.includes('deploy')) {
|
|
214
|
+
return 'Deployment completed';
|
|
215
|
+
}
|
|
216
|
+
return 'Workflow operation completed';
|
|
217
|
+
}
|
|
218
|
+
if (toolName.includes('health')) {
|
|
219
|
+
const status = result.status || result.overall_healthy;
|
|
220
|
+
return status === 'healthy' || status === true ? 'System healthy' : 'Issues detected';
|
|
221
|
+
}
|
|
222
|
+
if (toolName.includes('validate')) {
|
|
223
|
+
if (result.overall_compliance) {
|
|
224
|
+
return `Validation: ${result.overall_compliance}`;
|
|
225
|
+
}
|
|
226
|
+
return 'Validation completed';
|
|
227
|
+
}
|
|
228
|
+
return 'Operation completed';
|
|
229
|
+
}
|
|
230
|
+
async function bootstrap() {
|
|
231
|
+
const server = new McpServer({
|
|
232
|
+
name: "bob-mcp",
|
|
233
|
+
version: "0.1.0"
|
|
234
|
+
});
|
|
235
|
+
// Initialize MCP logging with error handling
|
|
236
|
+
try {
|
|
237
|
+
mcpLogger.initialize(server.server);
|
|
238
|
+
console.log("MCP logging initialized successfully");
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
console.error("Failed to initialize MCP logging:", error);
|
|
242
|
+
// Continue without logging rather than failing completely
|
|
243
|
+
}
|
|
244
|
+
// Initialize health checks
|
|
245
|
+
await initializeHealthChecks();
|
|
246
|
+
// Enhanced tool call wrapper with reliability features and modern CLI output
|
|
247
|
+
const withToolLogging = (toolName, handler) => {
|
|
248
|
+
return withErrorHandling(async (args, extra) => {
|
|
249
|
+
const startTime = Date.now();
|
|
250
|
+
const specId = (args && typeof args === 'object' && 'spec_id' in args) ? args.spec_id : undefined;
|
|
251
|
+
// Start logical operation for tool call
|
|
252
|
+
const operationId = await mcpLogger.startLogicalOperation('tool_call', specId, `🔧 Executing ${toolName}`);
|
|
253
|
+
// Generate enhanced status message for Claude Code terminal
|
|
254
|
+
const statusMessage = generateToolStatusMessage(toolName, args, 'started');
|
|
255
|
+
console.log(statusMessage);
|
|
256
|
+
await mcpLogger.logToolCall(toolName, args, specId);
|
|
257
|
+
try {
|
|
258
|
+
// Apply timeout to tool operations (30 seconds for most tools)
|
|
259
|
+
const timeoutMs = getToolTimeout(toolName);
|
|
260
|
+
const result = await withTimeout(() => handler(args, extra), { timeout: timeoutMs });
|
|
261
|
+
const duration = Date.now() - startTime;
|
|
262
|
+
// Add success sub-event
|
|
263
|
+
await mcpLogger.addSubEvent(operationId, {
|
|
264
|
+
timestamp: new Date().toISOString(),
|
|
265
|
+
event: 'tool_success',
|
|
266
|
+
details: { tool_name: toolName, duration_ms: duration }
|
|
267
|
+
});
|
|
268
|
+
// Generate success status message
|
|
269
|
+
const successMessage = generateToolStatusMessage(toolName, args, 'completed', duration, result);
|
|
270
|
+
console.log(successMessage);
|
|
271
|
+
await mcpLogger.logToolSuccess(toolName, duration, specId);
|
|
272
|
+
await mcpLogger.completeLogicalOperation(operationId, true);
|
|
273
|
+
// Return enhanced result with status summary
|
|
274
|
+
return {
|
|
275
|
+
...result,
|
|
276
|
+
_meta: {
|
|
277
|
+
tool: toolName,
|
|
278
|
+
duration_ms: duration,
|
|
279
|
+
status: 'success',
|
|
280
|
+
summary: extractResultSummary(toolName, result)
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
const duration = Date.now() - startTime;
|
|
286
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
287
|
+
// Add error sub-event
|
|
288
|
+
await mcpLogger.addSubEvent(operationId, {
|
|
289
|
+
timestamp: new Date().toISOString(),
|
|
290
|
+
event: 'tool_error',
|
|
291
|
+
details: { tool_name: toolName, error: errorMsg }
|
|
292
|
+
});
|
|
293
|
+
// Generate error status message
|
|
294
|
+
const errorMessage = generateToolStatusMessage(toolName, args, 'error', duration, null, errorMsg);
|
|
295
|
+
console.log(errorMessage);
|
|
296
|
+
await mcpLogger.logToolError(toolName, errorMsg, duration, specId);
|
|
297
|
+
await mcpLogger.completeLogicalOperation(operationId, false);
|
|
298
|
+
// Auto-debug if we have a spec_id and this is a critical error
|
|
299
|
+
if (specId && shouldInvokeDebugger(error)) {
|
|
300
|
+
try {
|
|
301
|
+
await autoDebugError({
|
|
302
|
+
spec_id: specId,
|
|
303
|
+
operation: `MCP tool execution: ${toolName}`,
|
|
304
|
+
error: error,
|
|
305
|
+
context: { args, toolName, duration }
|
|
306
|
+
});
|
|
307
|
+
console.log(`[AUTO-DEBUG] Created debug log for ${toolName} error in spec ${specId}`);
|
|
308
|
+
}
|
|
309
|
+
catch (debugError) {
|
|
310
|
+
console.log(`[AUTO-DEBUG] Failed to create debug log: ${debugError}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
}, `MCP tool: ${toolName}`, (args) => ({
|
|
316
|
+
tool_name: toolName,
|
|
317
|
+
spec_id: (args && typeof args === 'object' && 'spec_id' in args) ? args.spec_id : undefined
|
|
318
|
+
}));
|
|
319
|
+
};
|
|
320
|
+
// Register orchestrator tool (main entry point)
|
|
321
|
+
server.registerTool("bob.workshop", {
|
|
322
|
+
title: "🔧 Intelligent workflow orchestrator for all development tasks",
|
|
323
|
+
description: "🔧 Main entry point for all development workflows. Analyzes problems, asks clarifying questions when needed, and routes to appropriate modes (Architect, Engineer, Debugger, Reviewer). Use this as your primary tool for any development task.",
|
|
324
|
+
inputSchema: {
|
|
325
|
+
problem: z.string().min(5).describe("Problem statement or task description (e.g. 'Add JWT authentication', 'Fix login bug', 'Review user service')"),
|
|
326
|
+
mode: z.enum(["architect", "engineer", "debugger", "reviewer"]).optional().describe("Preferred mode (optional - will be auto-determined based on problem)"),
|
|
327
|
+
spec_id: z.string().optional().describe("Continue work on existing manual (optional)"),
|
|
328
|
+
clarifications: z.record(z.string()).optional().describe("Answers to previous clarifying questions")
|
|
329
|
+
}
|
|
330
|
+
}, withToolLogging("bob.workshop", async ({ problem, mode, spec_id, clarifications }) => {
|
|
331
|
+
console.log("bob.workshop orchestrator called with:", { problem, mode, spec_id, clarifications });
|
|
332
|
+
const result = await workshopHandler({ problem, mode, spec_id, clarifications });
|
|
333
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
334
|
+
}));
|
|
335
|
+
// Register Manual management tools (new workshop terminology)
|
|
336
|
+
server.registerTool("bob.manual.create", {
|
|
337
|
+
title: "🔧 Start a new feature manual for this project",
|
|
338
|
+
description: "🔧 Start a new feature manual for this project. Use this when building new apps, features, or components. This creates the manual that drives planning, coding, debugging, and review.",
|
|
339
|
+
inputSchema: {
|
|
340
|
+
title: z.string().describe("Title of the feature/app to build (e.g. 'Sudoku PWA', 'User Authentication')"),
|
|
341
|
+
author: z.string().optional().describe("Author of the manual"),
|
|
342
|
+
category: z.string().optional().describe("Category: frontend, backend, fullstack, devtools, etc."),
|
|
343
|
+
priority: z.string().optional().describe("Priority: low, medium, high, critical"),
|
|
344
|
+
tags: z.array(z.string()).optional().describe("Tags for organization (e.g. ['pwa', 'mobile', 'game'])")
|
|
345
|
+
}
|
|
346
|
+
}, withToolLogging("bob.manual.create", async ({ title, author, category, priority, tags }) => {
|
|
347
|
+
console.log("bob.manual.create MCP tool called with:", { title, author, category, priority, tags });
|
|
348
|
+
const result = await specCreateHandler({ title, author, category, priority, tags, initial_state: "draft" });
|
|
349
|
+
const conciseText = await formatConciseResponse("bob.manual.create", result);
|
|
350
|
+
return { content: [{ type: "text", text: conciseText }] };
|
|
351
|
+
}));
|
|
352
|
+
server.registerTool("bob.manual.update", {
|
|
353
|
+
title: "🔧 Update details or logs in a manual",
|
|
354
|
+
description: "🔧 Update details or logs in a manual (implementation notes, debug entries, etc.). Use this to add progress updates, execution logs, or debug information to an existing manual.",
|
|
355
|
+
inputSchema: {
|
|
356
|
+
spec_id: z.string().describe("Manual ID to update"),
|
|
357
|
+
section: z.enum(["executive_summary", "product_specifications", "architecture_analysis", "implementation_plan", "testing", "risk_assessment", "review"]).optional().describe("Section to update"),
|
|
358
|
+
content: z.string().optional().describe("Content to add to the section"),
|
|
359
|
+
execution_log: z.object({
|
|
360
|
+
timestamp: z.string(),
|
|
361
|
+
engineer: z.string(),
|
|
362
|
+
action: z.string(),
|
|
363
|
+
task_id: z.string(),
|
|
364
|
+
files_changed: z.array(z.string()),
|
|
365
|
+
commit_hash: z.string(),
|
|
366
|
+
note: z.string()
|
|
367
|
+
}).optional().describe("Execution log entry"),
|
|
368
|
+
debug_log: z.object({
|
|
369
|
+
timestamp: z.string(),
|
|
370
|
+
issue: z.string(),
|
|
371
|
+
root_cause: z.string(),
|
|
372
|
+
fix: z.string(),
|
|
373
|
+
confidence: z.string()
|
|
374
|
+
}).optional().describe("Debug log entry"),
|
|
375
|
+
state: z.enum(["draft", "ready", "engineered", "done"]).optional().describe("Manual state to set explicitly")
|
|
376
|
+
}
|
|
377
|
+
}, withToolLogging("bob.manual.update", async ({ spec_id, section, content, execution_log, debug_log, state }) => {
|
|
378
|
+
const result = await specUpdateHandler({ spec_id, section, content, execution_log, debug_log, state });
|
|
379
|
+
// Detect mode based on what type of update this is
|
|
380
|
+
let mode = 'unknown';
|
|
381
|
+
if (execution_log)
|
|
382
|
+
mode = 'engineer';
|
|
383
|
+
else if (debug_log)
|
|
384
|
+
mode = 'debugger';
|
|
385
|
+
else if (section === 'architecture_analysis' || section === 'product_specifications')
|
|
386
|
+
mode = 'architect';
|
|
387
|
+
else if (section === 'review')
|
|
388
|
+
mode = 'reviewer';
|
|
389
|
+
const conciseText = await formatConciseResponse("bob.manual.update", result);
|
|
390
|
+
return { content: [{ type: "text", text: conciseText }] };
|
|
391
|
+
}));
|
|
392
|
+
server.registerTool("bob.manual.get", {
|
|
393
|
+
title: "🔧 Retrieve a specific manual",
|
|
394
|
+
description: "🔧 Retrieve a specific manual with all its details, logs, and current status. Use this to review existing manuals before making updates or continuing work.",
|
|
395
|
+
inputSchema: {
|
|
396
|
+
spec_id: z.string().describe("Manual ID to retrieve")
|
|
397
|
+
}
|
|
398
|
+
}, withToolLogging("bob.manual.get", async ({ spec_id }) => {
|
|
399
|
+
const result = await specGetHandler({ spec_id });
|
|
400
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
401
|
+
}));
|
|
402
|
+
server.registerTool("bob.manual.list", {
|
|
403
|
+
title: "🔧 List all project manuals",
|
|
404
|
+
description: "🔧 List all project manuals with their current status. Use this to see what features/components are planned, in progress, or completed.",
|
|
405
|
+
inputSchema: {
|
|
406
|
+
filter: z.string().optional().describe("Optional filter by title or category")
|
|
407
|
+
}
|
|
408
|
+
}, async ({ filter }) => {
|
|
409
|
+
const result = await specListHandler({ filter });
|
|
410
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
411
|
+
});
|
|
412
|
+
// REMOVED: Legacy bob.spec.* tools - use bob.manual.* instead
|
|
413
|
+
// REMOVED: Individual worktree and git tools - use bob.workflow.* instead
|
|
414
|
+
// The following tools have been consolidated into bob.workflow.start and bob.workflow.deploy:
|
|
415
|
+
// - bob.worktree.list (use bob.worktree.debug with action: "status" instead)
|
|
416
|
+
// - bob.git.commit (automated by bob.workflow.deploy)
|
|
417
|
+
// - bob.git.merge (automated by bob.workflow.deploy)
|
|
418
|
+
// Advanced worktree maintenance tool (kept for debugging)
|
|
419
|
+
server.registerTool("bob.worktree.debug", {
|
|
420
|
+
title: "🔧 Debug and maintain worktrees",
|
|
421
|
+
description: "🔧 Comprehensive worktree maintenance tool. Handles repair (fix orphaned worktrees), cleanup (remove stale worktrees), validate (check worktree status), and status (detailed report). Essential for fail-safe worktree management.",
|
|
422
|
+
inputSchema: {
|
|
423
|
+
action: z.enum(["repair", "cleanup", "validate", "status"]).describe("Debug action: 'status' = report all worktrees, 'validate' = check manual worktree, 'repair' = fix orphaned worktrees, 'cleanup' = remove stale worktrees"),
|
|
424
|
+
spec_id: z.string().optional().describe("Optional manual ID for validate/repair actions"),
|
|
425
|
+
max_age_days: z.number().optional().default(7).describe("Max age in days for cleanup action (default: 7)")
|
|
426
|
+
}
|
|
427
|
+
}, withToolLogging("bob.worktree.debug", async ({ action, spec_id, max_age_days = 7 }) => {
|
|
428
|
+
const { worktreeDebugHandler } = await import("./tools/worktreeTools.js");
|
|
429
|
+
const result = await worktreeDebugHandler({ action, spec_id, max_age_days });
|
|
430
|
+
const conciseText = await formatConciseResponse("bob.worktree.debug", result);
|
|
431
|
+
return { content: [{ type: "text", text: conciseText }] };
|
|
432
|
+
}));
|
|
433
|
+
// REMOVED: Old search tools - use bob.code.search instead
|
|
434
|
+
server.registerTool("bob.code.search", {
|
|
435
|
+
title: "🔧 Unified lexical + semantic search",
|
|
436
|
+
description: "🔧 Hybrid search combining ripgrep (lexical) and semgrep (semantic) for comprehensive code search. Primary search tool with phase-aware execution and normalized outputs.",
|
|
437
|
+
inputSchema: {
|
|
438
|
+
query: z.string().describe("Search query to find in codebase"),
|
|
439
|
+
mode: z.enum(["lexical", "semantic", "auto"]).optional().default("auto").describe("Search mode: lexical (ripgrep only), semantic (semgrep only), or auto (both with smart scoping)"),
|
|
440
|
+
phase: z.enum(["architect", "engineer", "debugger", "reviewer"]).optional().describe("Caller phase for phase-aware Semgrep rule selection"),
|
|
441
|
+
path: z.string().optional().describe("Root path or subdirectory to search (optional)"),
|
|
442
|
+
includeHidden: z.boolean().optional().default(false).describe("Include hidden/ignored files (equivalent to rg -uuu)"),
|
|
443
|
+
followGitIgnore: z.boolean().optional().default(true).describe("Respect .gitignore rules (default: true)"),
|
|
444
|
+
fileTypes: z.array(z.string()).optional().describe("File types to include (e.g., ['ts', 'tsx', 'py'])"),
|
|
445
|
+
maxHits: z.number().optional().default(200).describe("Maximum total results across lexical and semantic"),
|
|
446
|
+
contextLines: z.number().optional().default(2).describe("Context lines before/after matches"),
|
|
447
|
+
timeoutMs: z.number().optional().default(4000).describe("Timeout in milliseconds")
|
|
448
|
+
}
|
|
449
|
+
}, async ({ query, mode, phase, path, includeHidden, followGitIgnore, fileTypes, maxHits, contextLines, timeoutMs }) => {
|
|
450
|
+
const result = await hybridSearchHandler({
|
|
451
|
+
query,
|
|
452
|
+
mode: mode || "auto",
|
|
453
|
+
phase,
|
|
454
|
+
path,
|
|
455
|
+
includeHidden: includeHidden || false,
|
|
456
|
+
followGitIgnore: followGitIgnore !== false,
|
|
457
|
+
fileTypes,
|
|
458
|
+
maxHits: maxHits || 200,
|
|
459
|
+
contextLines: contextLines || 2,
|
|
460
|
+
timeoutMs: timeoutMs || 4000
|
|
461
|
+
});
|
|
462
|
+
// Keep search results verbose since Claude needs to see the content
|
|
463
|
+
// But add concise summary to _meta for status messages
|
|
464
|
+
const enhancedResult = {
|
|
465
|
+
...result,
|
|
466
|
+
_meta: {
|
|
467
|
+
...(result._meta || {}),
|
|
468
|
+
summary: formatConciseResponse("bob.code.search", result)
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
return {
|
|
472
|
+
content: [{
|
|
473
|
+
type: "text",
|
|
474
|
+
text: JSON.stringify(enhancedResult, null, 2)
|
|
475
|
+
}]
|
|
476
|
+
};
|
|
477
|
+
});
|
|
478
|
+
// Register dashboard tools
|
|
479
|
+
server.registerTool("bob.dashboard.launch", {
|
|
480
|
+
title: "🔧 Open the visual workshop management interface",
|
|
481
|
+
description: "🔧 Launch the visual workshop dashboard for tracking manuals, worktrees, and logs on port 4577. Orchestrator should launch this when starting new feature development. Architect MUST launch this when working on new manuals. Returns status: 'launched', 'already_running', or 'already_running_external'. Process exits cleanly without hanging."
|
|
482
|
+
}, async () => {
|
|
483
|
+
const result = await dashboardLaunchHandler();
|
|
484
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
485
|
+
});
|
|
486
|
+
server.registerTool("bob.dashboard.update", {
|
|
487
|
+
title: "🔧 Send real-time updates to workshop dashboard",
|
|
488
|
+
description: "🔧 Push update to dashboard. Send real-time notifications to the dashboard about manual progress, server status, or other development events.",
|
|
489
|
+
inputSchema: {
|
|
490
|
+
spec_id: z.string().describe("Manual ID to send update for"),
|
|
491
|
+
event: z.string().describe("Event description (e.g., 'server started', 'implementation completed')"),
|
|
492
|
+
data: z.object({}).optional().describe("Additional event data")
|
|
493
|
+
}
|
|
494
|
+
}, async ({ spec_id, event, data }) => {
|
|
495
|
+
const result = await dashboardUpdateHandler({ spec_id, event, data });
|
|
496
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
497
|
+
});
|
|
498
|
+
// REMOVED: Approval tools - use structured outputs with approval_required flag instead
|
|
499
|
+
// Register Simplified Workflow tools (bundled git + worktree operations)
|
|
500
|
+
server.registerTool("bob.workflow.start", {
|
|
501
|
+
title: "🔧 Start new feature development workflow",
|
|
502
|
+
description: "🔧 Architect workflow: Create manual + worktree + launch dashboard. Bundles manual creation, git worktree setup, and dashboard launch for streamlined feature development start.",
|
|
503
|
+
inputSchema: {
|
|
504
|
+
title: z.string().describe("Title of the feature/app to build"),
|
|
505
|
+
author: z.string().optional().describe("Author of the manual"),
|
|
506
|
+
category: z.string().optional().describe("Category: frontend, backend, fullstack, devtools, etc."),
|
|
507
|
+
priority: z.string().optional().describe("Priority: low, medium, high, critical"),
|
|
508
|
+
tags: z.array(z.string()).optional().describe("Tags for organization"),
|
|
509
|
+
branch_name: z.string().optional().describe("Custom branch name (auto-generated if not provided)")
|
|
510
|
+
}
|
|
511
|
+
}, withToolLogging("bob.workflow.start", async ({ title, author, category, priority, tags, branch_name }) => {
|
|
512
|
+
console.log("bob.workflow.start called with:", { title, author, category, priority, tags, branch_name });
|
|
513
|
+
// Create manual
|
|
514
|
+
const manual = await specCreateHandler({ title, author, category, priority, tags, initial_state: "draft" });
|
|
515
|
+
// Create worktree with auto-generated branch name if not provided
|
|
516
|
+
const branchName = branch_name || `feature/${title.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '')}`;
|
|
517
|
+
const worktree = await worktreeCreateHandler({ spec_id: manual.spec_id, branch: branchName });
|
|
518
|
+
// Launch dashboard
|
|
519
|
+
const dashboard = await dashboardLaunchHandler();
|
|
520
|
+
// Send dashboard notification
|
|
521
|
+
await dashboardUpdateHandler({
|
|
522
|
+
spec_id: manual.spec_id,
|
|
523
|
+
event: "workflow_started",
|
|
524
|
+
data: { title, branch: branchName, dashboard_url: "http://localhost:4577" }
|
|
525
|
+
});
|
|
526
|
+
// Log workflow initialization via execution log
|
|
527
|
+
await specUpdateHandler({
|
|
528
|
+
spec_id: manual.spec_id,
|
|
529
|
+
execution_log: {
|
|
530
|
+
timestamp: new Date().toISOString(),
|
|
531
|
+
engineer: "architect",
|
|
532
|
+
action: "planning: milestone_completed",
|
|
533
|
+
task_id: "WORKFLOW_INIT",
|
|
534
|
+
files_changed: [`manual: ${manual.spec_id}`, `worktree: ${branchName}`, "dashboard: launched"],
|
|
535
|
+
commit_hash: "pending",
|
|
536
|
+
note: "[planning] 🎯 Milestone: Feature development workflow started. Deliverables: manual: " + manual.spec_id + ", worktree: " + branchName + ", dashboard: launched. Next: complete requirements analysis, create implementation plan, start development"
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
// NOTE: Dev server startup moved to post-test phase in Engineer workflow
|
|
540
|
+
// This prevents early resource conflicts and aligns with 3-gate approval system
|
|
541
|
+
const workflowResult = {
|
|
542
|
+
manual: manual,
|
|
543
|
+
worktree: worktree,
|
|
544
|
+
dashboard: dashboard,
|
|
545
|
+
workflow_status: "started",
|
|
546
|
+
work_recording: "active",
|
|
547
|
+
note: "Dev server will be started after implementation and testing"
|
|
548
|
+
};
|
|
549
|
+
const conciseText = await formatConciseResponse("bob.workflow.start", workflowResult);
|
|
550
|
+
return {
|
|
551
|
+
content: [{
|
|
552
|
+
type: "text",
|
|
553
|
+
text: conciseText
|
|
554
|
+
}]
|
|
555
|
+
};
|
|
556
|
+
}));
|
|
557
|
+
server.registerTool("bob.workflow.deploy", {
|
|
558
|
+
title: "🔧 Complete implementation and deploy to main",
|
|
559
|
+
description: "🔧 Engineer workflow: Commit changes + merge to main + cleanup worktree. Bundles git operations for streamlined feature completion and deployment.",
|
|
560
|
+
inputSchema: {
|
|
561
|
+
spec_id: z.string().describe("Manual ID for the completed implementation"),
|
|
562
|
+
action_description: z.string().describe("Brief description of what was implemented"),
|
|
563
|
+
files_changed: z.array(z.string()).optional().describe("List of files changed (auto-detected if not provided)"),
|
|
564
|
+
target_branch: z.string().optional().default("main").describe("Target branch to merge into")
|
|
565
|
+
}
|
|
566
|
+
}, withToolLogging("bob.workflow.deploy", async ({ spec_id, action_description, files_changed, target_branch = "main" }) => {
|
|
567
|
+
console.log("bob.workflow.deploy called with:", { spec_id, action_description, files_changed, target_branch });
|
|
568
|
+
// Get git status and auto-detect changed files if not provided
|
|
569
|
+
const gitStatus = await getGitStatus();
|
|
570
|
+
const changedFiles = files_changed || [...gitStatus.staged_files, ...gitStatus.unstaged_files, ...gitStatus.untracked_files];
|
|
571
|
+
// NEW: Stop any running dev server before deployment
|
|
572
|
+
let serverCleanupInfo = null;
|
|
573
|
+
try {
|
|
574
|
+
console.log(`Stopping dev server for manual ${spec_id}...`);
|
|
575
|
+
const serverStopped = await stopDevServer(spec_id);
|
|
576
|
+
if (serverStopped) {
|
|
577
|
+
serverCleanupInfo = { server_stopped: true };
|
|
578
|
+
console.log(`Dev server stopped successfully for manual ${spec_id}`);
|
|
579
|
+
// Log server cleanup
|
|
580
|
+
await specUpdateHandler({
|
|
581
|
+
spec_id,
|
|
582
|
+
execution_log: {
|
|
583
|
+
timestamp: new Date().toISOString(),
|
|
584
|
+
engineer: "system",
|
|
585
|
+
action: "server_stopped",
|
|
586
|
+
task_id: "auto-server-cleanup",
|
|
587
|
+
files_changed: [],
|
|
588
|
+
commit_hash: "pending",
|
|
589
|
+
note: `Auto-stopped dev server before deployment`
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
serverCleanupInfo = { server_stopped: false, reason: "no_server_running" };
|
|
595
|
+
console.log(`No running server found for manual ${spec_id}`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
catch (serverError) {
|
|
599
|
+
console.warn('Server cleanup failed (non-critical):', serverError);
|
|
600
|
+
serverCleanupInfo = { server_stopped: false, error: String(serverError) };
|
|
601
|
+
}
|
|
602
|
+
// Find worktree path for this spec
|
|
603
|
+
const worktreePath = await findWorktreeForSpec(spec_id);
|
|
604
|
+
console.log(`Found worktree path: ${worktreePath}`);
|
|
605
|
+
// Commit changes
|
|
606
|
+
const commit = await autoCommitChanges({
|
|
607
|
+
spec_id,
|
|
608
|
+
files_changed: changedFiles,
|
|
609
|
+
action_description,
|
|
610
|
+
worktree_path: worktreePath || undefined
|
|
611
|
+
});
|
|
612
|
+
// Merge and cleanup
|
|
613
|
+
const merge = await autoMergeAndCleanup({
|
|
614
|
+
spec_id,
|
|
615
|
+
target_branch,
|
|
616
|
+
cleanup_worktree: true
|
|
617
|
+
});
|
|
618
|
+
// Send dashboard notification
|
|
619
|
+
await dashboardUpdateHandler({
|
|
620
|
+
spec_id,
|
|
621
|
+
event: "workflow_deployed",
|
|
622
|
+
data: {
|
|
623
|
+
action_description,
|
|
624
|
+
files_changed: changedFiles,
|
|
625
|
+
target_branch,
|
|
626
|
+
commit_hash: commit.commit_hash
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
// Record completion milestone
|
|
630
|
+
await specUpdateHandler({
|
|
631
|
+
spec_id: spec_id,
|
|
632
|
+
execution_log: {
|
|
633
|
+
timestamp: new Date().toISOString(),
|
|
634
|
+
engineer: "engineer",
|
|
635
|
+
action: "implementation: workflow_deployment_completed",
|
|
636
|
+
task_id: "WORKFLOW_DEPLOY",
|
|
637
|
+
files_changed: changedFiles,
|
|
638
|
+
commit_hash: commit.commit_hash,
|
|
639
|
+
note: `🚀 Successfully deployed implementation: ${action_description}. Committed ${changedFiles.length} files and merged to ${target_branch}. Commit: ${commit.commit_hash}`
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
const deployResult = {
|
|
643
|
+
commit: commit,
|
|
644
|
+
merge: merge,
|
|
645
|
+
server_cleanup: serverCleanupInfo,
|
|
646
|
+
workflow_status: "deployed",
|
|
647
|
+
work_recording: "completed"
|
|
648
|
+
};
|
|
649
|
+
const conciseText = await formatConciseResponse("bob.workflow.deploy", deployResult);
|
|
650
|
+
return {
|
|
651
|
+
content: [{
|
|
652
|
+
type: "text",
|
|
653
|
+
text: conciseText
|
|
654
|
+
}]
|
|
655
|
+
};
|
|
656
|
+
}));
|
|
657
|
+
// Health check tool migrated to CLI: use "bobs health"
|
|
658
|
+
// Register change validation tool
|
|
659
|
+
server.registerTool("bob.validate.changes", {
|
|
660
|
+
title: "🔍 Validate implementation changes against manual",
|
|
661
|
+
description: "🔍 Validate recent file changes against manual expectations. Analyzes actual diffs, checks compliance, and provides human-readable summaries of what was implemented.",
|
|
662
|
+
inputSchema: {
|
|
663
|
+
spec_id: z.string().describe("Manual ID to validate changes against")
|
|
664
|
+
}
|
|
665
|
+
}, withToolLogging("bob.validate.changes", async ({ spec_id }) => {
|
|
666
|
+
console.log("bob.validate.changes called with:", { spec_id });
|
|
667
|
+
if (!spec_id) {
|
|
668
|
+
throw new Error("spec_id is required");
|
|
669
|
+
}
|
|
670
|
+
const validationResult = await changeValidator.validateChangesForSpec(spec_id);
|
|
671
|
+
// Update manual with validation results
|
|
672
|
+
await changeValidator.updateSpecWithValidation(validationResult);
|
|
673
|
+
return { content: [{ type: "text", text: JSON.stringify(validationResult, null, 2) }] };
|
|
674
|
+
}));
|
|
675
|
+
// Register implementation summarizer tool
|
|
676
|
+
server.registerTool("bob.summarize.implementation", {
|
|
677
|
+
title: "📊 Generate implementation summary from chokidar observations",
|
|
678
|
+
description: "📊 Analyze file changes observed by chokidar and generate comprehensive implementation summaries with compliance analysis, git commits, and actionable insights. Updates the manual's implementation logs.",
|
|
679
|
+
inputSchema: {
|
|
680
|
+
spec_id: z.string().describe("Manual ID to generate implementation summary for"),
|
|
681
|
+
lookback_hours: z.number().optional().default(24).describe("Hours to look back for changes (default: 24)")
|
|
682
|
+
}
|
|
683
|
+
}, withToolLogging("bob.summarize.implementation", async ({ spec_id, lookback_hours = 24 }) => {
|
|
684
|
+
console.log("bob.summarize.implementation called with:", { spec_id, lookback_hours });
|
|
685
|
+
if (!spec_id) {
|
|
686
|
+
throw new Error("spec_id is required");
|
|
687
|
+
}
|
|
688
|
+
const summary = await implementationSummarizer.generateSummaryForSpec(spec_id, lookback_hours);
|
|
689
|
+
// Update manual with summary
|
|
690
|
+
await implementationSummarizer.updateSpecWithSummary(summary);
|
|
691
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
692
|
+
}));
|
|
693
|
+
// Cleanup tools migrated to CLI: use "bobs cleanup" and "bobs cleanup --stats"
|
|
694
|
+
// Debug tool - no parameters
|
|
695
|
+
server.registerTool("bob.debug", {
|
|
696
|
+
title: "🔧 Debug Bob's Workshop server state",
|
|
697
|
+
description: "🔧 Debug tool with no parameters. Use this to check Bob's Workshop internal state and troubleshoot issues.",
|
|
698
|
+
}, async () => {
|
|
699
|
+
console.log("bob.debug called successfully");
|
|
700
|
+
return {
|
|
701
|
+
content: [
|
|
702
|
+
{
|
|
703
|
+
type: "text",
|
|
704
|
+
text: "Debug tool works!",
|
|
705
|
+
},
|
|
706
|
+
],
|
|
707
|
+
};
|
|
708
|
+
});
|
|
709
|
+
// Register prompts
|
|
710
|
+
server.registerPrompt("Architect", { description: "Requirements analysis and technical planning specialist" }, async () => ({
|
|
711
|
+
messages: [{ role: "user", content: [{ type: "text", text: ARCHITECT_PROMPT }] }]
|
|
712
|
+
}));
|
|
713
|
+
server.registerPrompt("Engineer", { description: "Implementation specialist following engineering best practices" }, async () => ({
|
|
714
|
+
messages: [{ role: "user", content: [{ type: "text", text: ENGINEER_PROMPT }] }]
|
|
715
|
+
}));
|
|
716
|
+
server.registerPrompt("Debugger", { description: "Issue diagnosis and resolution specialist" }, async () => ({
|
|
717
|
+
messages: [{ role: "user", content: [{ type: "text", text: DEBUGGER_PROMPT }] }]
|
|
718
|
+
}));
|
|
719
|
+
server.registerPrompt("Reviewer", { description: "Quality assurance and security audit specialist" }, async () => ({
|
|
720
|
+
messages: [{ role: "user", content: [{ type: "text", text: REVIEWER_PROMPT }] }]
|
|
721
|
+
}));
|
|
722
|
+
// Start auto-cleanup service
|
|
723
|
+
cleanupService.startAutoCleanup(30); // Every 30 minutes
|
|
724
|
+
console.log("Auto-cleanup service started (30-minute intervals)");
|
|
725
|
+
// Set up graceful shutdown handlers
|
|
726
|
+
const signals = ['SIGTERM', 'SIGINT', 'SIGUSR2'];
|
|
727
|
+
signals.forEach(signal => {
|
|
728
|
+
process.on(signal, async () => {
|
|
729
|
+
console.log(`Received ${signal}, initiating graceful shutdown`);
|
|
730
|
+
await gracefulShutdown(server);
|
|
731
|
+
process.exit(0);
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
// Handle uncaught exceptions and rejections
|
|
735
|
+
process.on('uncaughtException', async (error) => {
|
|
736
|
+
console.error('Uncaught Exception:', error);
|
|
737
|
+
globalErrorCollector.add(await import('./utils/errorHandling.js').then(m => m.enhanceError(error, m.createErrorContext('uncaught_exception'))));
|
|
738
|
+
await gracefulShutdown(server);
|
|
739
|
+
process.exit(1);
|
|
740
|
+
});
|
|
741
|
+
process.on('unhandledRejection', async (reason, promise) => {
|
|
742
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
743
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
744
|
+
globalErrorCollector.add(await import('./utils/errorHandling.js').then(m => m.enhanceError(error, m.createErrorContext('unhandled_rejection'))));
|
|
745
|
+
await gracefulShutdown(server);
|
|
746
|
+
process.exit(1);
|
|
747
|
+
});
|
|
748
|
+
console.log("Starting Bob MCP server with enhanced reliability...");
|
|
749
|
+
const transport = new StdioServerTransport();
|
|
750
|
+
await server.connect(transport);
|
|
751
|
+
console.log("Bob MCP server started successfully");
|
|
752
|
+
}
|
|
753
|
+
bootstrap().catch(err => {
|
|
754
|
+
console.error("Failed to start Bob MCP:", err);
|
|
755
|
+
process.exit(1);
|
|
756
|
+
});
|
|
757
|
+
//# sourceMappingURL=index.js.map
|