bobs-workshop 0.3.2 → 3.1.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/LICENSE +2 -2
- package/README.md +199 -210
- package/bin/bobs-workshop.js +109 -0
- package/config/agents.json +27 -0
- package/dist/plugins/bobs-workshop.js +34 -0
- package/dist/tools/background-agent/cancel.d.ts +3 -0
- package/dist/tools/background-agent/cancel.d.ts.map +1 -0
- package/dist/tools/background-agent/cancel.js +52 -0
- package/dist/tools/background-agent/concurrency.d.ts +15 -0
- package/dist/tools/background-agent/concurrency.d.ts.map +1 -0
- package/dist/tools/background-agent/concurrency.js +61 -0
- package/dist/tools/background-agent/index.d.ts +8 -0
- package/dist/tools/background-agent/index.d.ts.map +1 -0
- package/dist/tools/background-agent/index.js +7 -0
- package/dist/tools/background-agent/launch.d.ts +6 -0
- package/dist/tools/background-agent/launch.d.ts.map +1 -0
- package/dist/tools/background-agent/launch.js +33 -0
- package/dist/tools/background-agent/list.d.ts +7 -0
- package/dist/tools/background-agent/list.d.ts.map +1 -0
- package/dist/tools/background-agent/list.js +40 -0
- package/dist/tools/background-agent/manager.d.ts +29 -0
- package/dist/tools/background-agent/manager.d.ts.map +1 -0
- package/dist/tools/background-agent/manager.js +377 -0
- package/dist/tools/background-agent/output.d.ts +3 -0
- package/dist/tools/background-agent/output.d.ts.map +1 -0
- package/dist/tools/background-agent/output.js +41 -0
- package/dist/tools/background-agent/types.d.ts +46 -0
- package/dist/tools/background-agent/types.d.ts.map +1 -0
- package/dist/tools/background-agent/types.js +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/manual/index.d.ts +3 -0
- package/dist/tools/manual/index.d.ts.map +1 -0
- package/dist/tools/manual/index.js +2 -0
- package/dist/tools/manual/manual-update.d.ts +4 -0
- package/dist/tools/manual/manual-update.d.ts.map +1 -0
- package/dist/tools/manual/manual-update.js +190 -0
- package/dist/tools/manual/verify-manual.d.ts +4 -0
- package/dist/tools/manual/verify-manual.d.ts.map +1 -0
- package/dist/tools/manual/verify-manual.js +46 -0
- package/package.json +35 -67
- package/postinstall.js +190 -0
- package/src/agents/alice.md +466 -0
- package/src/agents/bob-rev.md +493 -0
- package/src/agents/bob-send.md +277 -0
- package/src/agents/bob.md +442 -0
- package/src/agents/trace.md +451 -0
- package/src/plugins/bobs-workshop.ts +45 -0
- package/src/skills/api-patterns/SKILL.md +376 -0
- package/src/skills/architecture/SKILL.md +271 -0
- package/src/skills/bobs-workshop/performance/icon.svg +3 -0
- package/src/skills/brainstorming/SKILL.md +210 -0
- package/src/skills/clean-code/SKILL.md +151 -0
- package/src/skills/code-review-checklist/SKILL.md +220 -0
- package/src/skills/database-design/SKILL.md +271 -0
- package/src/skills/exploration/SKILL.md +257 -0
- package/src/skills/frontend-ui-ux/SKILL.md +78 -0
- package/src/skills/git-master/SKILL.md +1105 -0
- package/src/skills/performance/SKILL.md +144 -0
- package/src/skills/performance/icon.svg +3 -0
- package/src/skills/plan-writing/SKILL.md +225 -0
- package/src/skills/security/SKILL.md +410 -0
- package/src/skills/simplification/SKILL.md +238 -0
- package/src/skills/systematic-debugging/SKILL.md +175 -0
- package/src/skills/testing-patterns/SKILL.md +305 -0
- package/src/skills/verification/SKILL.md +286 -0
- package/src/tools/background-agent/cancel.ts +67 -0
- package/src/tools/background-agent/concurrency.ts +71 -0
- package/src/tools/background-agent/index.ts +7 -0
- package/src/tools/background-agent/launch.ts +39 -0
- package/src/tools/background-agent/list.ts +50 -0
- package/src/tools/background-agent/manager.ts +455 -0
- package/src/tools/background-agent/output.ts +57 -0
- package/src/tools/background-agent/types.ts +55 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/manual/index.ts +2 -0
- package/src/tools/manual/manual-update.ts +197 -0
- package/src/tools/manual/verify-manual.ts +55 -0
- package/uninstall.js +64 -0
- package/Claude.md +0 -162
- package/bin/bobs-mcp-server.js +0 -11
- package/bin/bobs-mcp.js +0 -130
- package/dist/api/taskLogger.js +0 -106
- package/dist/api/taskLogger.js.map +0 -1
- package/dist/cli/checker.js +0 -401
- package/dist/cli/checker.js.map +0 -1
- package/dist/cli/cleanup.js +0 -131
- package/dist/cli/cleanup.js.map +0 -1
- package/dist/cli/debug.js +0 -157
- package/dist/cli/debug.js.map +0 -1
- package/dist/cli/health.js +0 -97
- package/dist/cli/health.js.map +0 -1
- package/dist/cli/setup.js +0 -81
- package/dist/cli/setup.js.map +0 -1
- package/dist/cli/workshop.js +0 -42
- package/dist/cli/workshop.js.map +0 -1
- package/dist/dashboard/server.js +0 -1203
- package/dist/dashboard/server.js.map +0 -1
- package/dist/index.js +0 -960
- package/dist/index.js.map +0 -1
- package/dist/prompts/architect.js +0 -221
- package/dist/prompts/architect.js.map +0 -1
- package/dist/prompts/debugger.js +0 -257
- package/dist/prompts/debugger.js.map +0 -1
- package/dist/prompts/engineer.js +0 -249
- package/dist/prompts/engineer.js.map +0 -1
- package/dist/prompts/orchestrator.js +0 -304
- package/dist/prompts/orchestrator.js.map +0 -1
- package/dist/prompts/reviewer.js +0 -289
- package/dist/prompts/reviewer.js.map +0 -1
- package/dist/services/activitySummarizer.js +0 -388
- package/dist/services/activitySummarizer.js.map +0 -1
- package/dist/services/changeValidator.js +0 -396
- package/dist/services/changeValidator.js.map +0 -1
- package/dist/services/claudeOrchestrator.js +0 -343
- package/dist/services/claudeOrchestrator.js.map +0 -1
- package/dist/services/fileMonitor.js +0 -250
- package/dist/services/fileMonitor.js.map +0 -1
- package/dist/services/implementationSummarizer.js +0 -306
- package/dist/services/implementationSummarizer.js.map +0 -1
- package/dist/services/liveMonitor.js +0 -315
- package/dist/services/liveMonitor.js.map +0 -1
- package/dist/services/mcpAuditLogger.js +0 -104
- package/dist/services/mcpAuditLogger.js.map +0 -1
- package/dist/services/mcpLogger.js +0 -223
- package/dist/services/mcpLogger.js.map +0 -1
- package/dist/services/tmuxManager.js +0 -541
- package/dist/services/tmuxManager.js.map +0 -1
- package/dist/tools/approvalTools.js +0 -244
- package/dist/tools/approvalTools.js.map +0 -1
- package/dist/tools/autoDebugger.js +0 -147
- package/dist/tools/autoDebugger.js.map +0 -1
- package/dist/tools/cleanupService.js +0 -221
- package/dist/tools/cleanupService.js.map +0 -1
- package/dist/tools/dashboardTools.js +0 -342
- package/dist/tools/dashboardTools.js.map +0 -1
- package/dist/tools/developmentNudges.js +0 -336
- package/dist/tools/developmentNudges.js.map +0 -1
- package/dist/tools/gitTools.js +0 -741
- package/dist/tools/gitTools.js.map +0 -1
- package/dist/tools/orchestratorTools.js +0 -832
- package/dist/tools/orchestratorTools.js.map +0 -1
- package/dist/tools/searchCache.js +0 -64
- package/dist/tools/searchCache.js.map +0 -1
- package/dist/tools/searchTools.js +0 -1107
- package/dist/tools/searchTools.js.map +0 -1
- package/dist/tools/semgrep-patterns.js +0 -296
- package/dist/tools/semgrep-patterns.js.map +0 -1
- package/dist/tools/specTools.js +0 -332
- package/dist/tools/specTools.js.map +0 -1
- package/dist/tools/structural/__tests__/orchestrator.test.js +0 -61
- package/dist/tools/structural/__tests__/orchestrator.test.js.map +0 -1
- package/dist/tools/structural/cache.js +0 -226
- package/dist/tools/structural/cache.js.map +0 -1
- package/dist/tools/structural/engines/python/index.js +0 -118
- package/dist/tools/structural/engines/python/index.js.map +0 -1
- package/dist/tools/structural/engines/typescript/__tests__/typescript-engine.test.js +0 -97
- package/dist/tools/structural/engines/typescript/__tests__/typescript-engine.test.js.map +0 -1
- package/dist/tools/structural/engines/typescript/analyzer.js +0 -433
- package/dist/tools/structural/engines/typescript/analyzer.js.map +0 -1
- package/dist/tools/structural/engines/typescript/index.js +0 -381
- package/dist/tools/structural/engines/typescript/index.js.map +0 -1
- package/dist/tools/structural/engines/typescript/utils.js +0 -279
- package/dist/tools/structural/engines/typescript/utils.js.map +0 -1
- package/dist/tools/structural/index.js +0 -248
- package/dist/tools/structural/index.js.map +0 -1
- package/dist/tools/structural/types.js +0 -18
- package/dist/tools/structural/types.js.map +0 -1
- package/dist/tools/tmuxTools.js +0 -100
- package/dist/tools/tmuxTools.js.map +0 -1
- package/dist/tools/workRecorder.js +0 -215
- package/dist/tools/workRecorder.js.map +0 -1
- package/dist/tools/worktreeTools.js +0 -705
- package/dist/tools/worktreeTools.js.map +0 -1
- package/dist/utils/__tests__/integration.test.js +0 -57
- package/dist/utils/__tests__/integration.test.js.map +0 -1
- package/dist/utils/__tests__/serverDetection.test.js +0 -151
- package/dist/utils/__tests__/serverDetection.test.js.map +0 -1
- package/dist/utils/errorHandling.js +0 -336
- package/dist/utils/errorHandling.js.map +0 -1
- package/dist/utils/processManager.js +0 -172
- package/dist/utils/processManager.js.map +0 -1
- package/dist/utils/reliability.js +0 -263
- package/dist/utils/reliability.js.map +0 -1
- package/dist/utils/responseFormatter.js +0 -250
- package/dist/utils/responseFormatter.js.map +0 -1
- package/dist/utils/serverDetection.js +0 -133
- package/dist/utils/serverDetection.js.map +0 -1
- package/dist/utils/specMigration.js +0 -105
- package/dist/utils/specMigration.js.map +0 -1
- package/dist/validation/schemas.js +0 -299
- package/dist/validation/schemas.js.map +0 -1
- package/public/.well-known/mcp/manifest.json +0 -473
- package/public/index.html +0 -3157
- package/public/index.html.backup +0 -2805
- package/public/index.html.backup2 +0 -1292
- package/scripts/cleanup-system-logs.ts +0 -121
- package/scripts/init-workspace.js +0 -63
- package/scripts/install-search-tools.js +0 -116
package/dist/index.js
DELETED
|
@@ -1,960 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// src/index.ts
|
|
3
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
import { ListResourcesRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
8
|
-
import path from "path";
|
|
9
|
-
import fs from "fs-extra";
|
|
10
|
-
// Import all tool handlers
|
|
11
|
-
import { specCreateHandler, specUpdateHandler, specGetHandler, specListHandler } from "./tools/specTools.js";
|
|
12
|
-
import { worktreeCreateHandler } from "./tools/worktreeTools.js";
|
|
13
|
-
import { stopDevServer } from "./utils/processManager.js";
|
|
14
|
-
import { hybridSearchHandler } from "./tools/searchTools.js";
|
|
15
|
-
import { dashboardLaunchHandler } from "./tools/dashboardTools.js";
|
|
16
|
-
import { workshopHandler, formatRoleDirective } from "./tools/orchestratorTools.js";
|
|
17
|
-
// REMOVED: Approval tools imports - use structured outputs instead
|
|
18
|
-
import { getGitStatus, autoCommitChanges, autoMergeAndCleanup, findWorktreeForSpec } from "./tools/gitTools.js";
|
|
19
|
-
import { autoDebugError, shouldInvokeDebugger } from "./tools/autoDebugger.js";
|
|
20
|
-
import { cleanupService } from "./tools/cleanupService.js";
|
|
21
|
-
import { changeValidator } from "./services/changeValidator.js";
|
|
22
|
-
import { implementationSummarizer } from "./services/implementationSummarizer.js";
|
|
23
|
-
import { mcpLogger } from "./services/mcpLogger.js";
|
|
24
|
-
import { formatConciseResponse } from "./utils/responseFormatter.js";
|
|
25
|
-
import { withTimeout, createCircuitBreaker, globalHealthChecker } from "./utils/reliability.js";
|
|
26
|
-
import { withErrorHandling, globalErrorCollector } from "./utils/errorHandling.js";
|
|
27
|
-
// Import all prompts
|
|
28
|
-
import { ARCHITECT_PROMPT } from "./prompts/architect.js";
|
|
29
|
-
import { ENGINEER_PROMPT } from "./prompts/engineer.js";
|
|
30
|
-
import { DEBUGGER_PROMPT } from "./prompts/debugger.js";
|
|
31
|
-
import { REVIEWER_PROMPT } from "./prompts/reviewer.js";
|
|
32
|
-
// Global circuit breakers for external operations
|
|
33
|
-
const fileSystemCircuitBreaker = createCircuitBreaker({
|
|
34
|
-
threshold: 5,
|
|
35
|
-
timeout: 15000,
|
|
36
|
-
resetTimeout: 30000
|
|
37
|
-
});
|
|
38
|
-
// Helper function to determine appropriate timeout for different tools
|
|
39
|
-
function getToolTimeout(toolName) {
|
|
40
|
-
// Longer timeouts for complex operations
|
|
41
|
-
if (toolName.includes('search') || toolName.includes('hybrid')) {
|
|
42
|
-
return 45000; // 45 seconds for search operations
|
|
43
|
-
}
|
|
44
|
-
if (toolName.includes('workflow') || toolName.includes('create')) {
|
|
45
|
-
return 60000; // 60 seconds for workflow operations
|
|
46
|
-
}
|
|
47
|
-
return 20000; // 20 seconds default
|
|
48
|
-
}
|
|
49
|
-
// Initialize health checks for system components
|
|
50
|
-
async function initializeHealthChecks() {
|
|
51
|
-
// MCP server health check
|
|
52
|
-
globalHealthChecker.register('mcp_server', async () => {
|
|
53
|
-
try {
|
|
54
|
-
// Basic functionality check
|
|
55
|
-
return process.uptime() > 0;
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
// File system health check
|
|
62
|
-
globalHealthChecker.register('file_system', async () => {
|
|
63
|
-
try {
|
|
64
|
-
const fs = await import('fs-extra');
|
|
65
|
-
const path = await import('path');
|
|
66
|
-
const testPath = path.resolve(process.cwd(), '.bob');
|
|
67
|
-
await fs.ensureDir(testPath);
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
// Dashboard service check
|
|
75
|
-
globalHealthChecker.register('dashboard', async () => {
|
|
76
|
-
try {
|
|
77
|
-
// Check if we can create the dashboard server
|
|
78
|
-
const { createDashboardServer } = await import('./dashboard/server.js');
|
|
79
|
-
const app = createDashboardServer();
|
|
80
|
-
return app !== null;
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
console.log("Health checks initialized");
|
|
87
|
-
}
|
|
88
|
-
// Graceful shutdown handler
|
|
89
|
-
async function gracefulShutdown(server) {
|
|
90
|
-
console.log("Initiating graceful shutdown...");
|
|
91
|
-
try {
|
|
92
|
-
// Stop auto-cleanup service
|
|
93
|
-
const { cleanupService } = await import('./tools/cleanupService.js');
|
|
94
|
-
cleanupService.stopAutoCleanup();
|
|
95
|
-
console.log("Auto-cleanup service stopped");
|
|
96
|
-
// Perform final cleanup
|
|
97
|
-
await cleanupService.performCleanup({
|
|
98
|
-
maxWorktreeAge: 7,
|
|
99
|
-
cleanTempFiles: true,
|
|
100
|
-
stopOrphanedRecorders: true,
|
|
101
|
-
forceCleanup: false
|
|
102
|
-
});
|
|
103
|
-
console.log("Final cleanup completed");
|
|
104
|
-
// Log shutdown statistics
|
|
105
|
-
const errorStats = globalErrorCollector.getStats();
|
|
106
|
-
console.log("Error statistics:", errorStats);
|
|
107
|
-
console.log("Graceful shutdown completed");
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
console.error("Error during shutdown:", error);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Enhanced CLI output formatting for modern terminal experience
|
|
114
|
-
function generateToolStatusMessage(toolName, args, status, duration, result, error) {
|
|
115
|
-
const toolCategory = getToolCategory(toolName);
|
|
116
|
-
const categoryIcon = getCategoryIcon(toolCategory);
|
|
117
|
-
const statusIcon = getStatusIcon(status);
|
|
118
|
-
const toolDisplayName = getToolDisplayName(toolName);
|
|
119
|
-
switch (status) {
|
|
120
|
-
case 'started':
|
|
121
|
-
return `${categoryIcon} [${toolCategory.toUpperCase()}] ${toolDisplayName} → ⏳ Starting...`;
|
|
122
|
-
case 'completed':
|
|
123
|
-
const durationText = formatDuration(duration || 0);
|
|
124
|
-
const summary = extractResultSummary(toolName, result);
|
|
125
|
-
return `${categoryIcon} [${toolCategory.toUpperCase()}] ${toolDisplayName} → ${statusIcon} ${summary} (${durationText})`;
|
|
126
|
-
case 'error':
|
|
127
|
-
const errorDuration = formatDuration(duration || 0);
|
|
128
|
-
return `${categoryIcon} [${toolCategory.toUpperCase()}] ${toolDisplayName} → ❌ Failed: ${error} (${errorDuration})`;
|
|
129
|
-
default:
|
|
130
|
-
return `${categoryIcon} [${toolCategory.toUpperCase()}] ${toolDisplayName}`;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
function getToolCategory(toolName) {
|
|
134
|
-
if (toolName.includes('manual'))
|
|
135
|
-
return 'manual';
|
|
136
|
-
if (toolName.includes('search'))
|
|
137
|
-
return 'search';
|
|
138
|
-
if (toolName.includes('workflow'))
|
|
139
|
-
return 'workflow';
|
|
140
|
-
if (toolName.includes('dashboard'))
|
|
141
|
-
return 'dashboard';
|
|
142
|
-
if (toolName.includes('worktree'))
|
|
143
|
-
return 'worktree';
|
|
144
|
-
if (toolName.includes('validate'))
|
|
145
|
-
return 'validation';
|
|
146
|
-
if (toolName.includes('summarize'))
|
|
147
|
-
return 'analysis';
|
|
148
|
-
if (toolName.includes('health'))
|
|
149
|
-
return 'system';
|
|
150
|
-
if (toolName.includes('cleanup'))
|
|
151
|
-
return 'maintenance';
|
|
152
|
-
if (toolName.includes('workshop'))
|
|
153
|
-
return 'orchestration';
|
|
154
|
-
return 'tool';
|
|
155
|
-
}
|
|
156
|
-
function getCategoryIcon(category) {
|
|
157
|
-
const icons = {
|
|
158
|
-
'manual': '📋',
|
|
159
|
-
'search': '🔍',
|
|
160
|
-
'workflow': '🔄',
|
|
161
|
-
'dashboard': '💻',
|
|
162
|
-
'worktree': '🌳',
|
|
163
|
-
'validation': '✅',
|
|
164
|
-
'analysis': '📊',
|
|
165
|
-
'system': '🛠️',
|
|
166
|
-
'maintenance': '🧹',
|
|
167
|
-
'session': '📺',
|
|
168
|
-
'orchestration': '🎯',
|
|
169
|
-
'tool': '⚙️'
|
|
170
|
-
};
|
|
171
|
-
return icons[category] || '⚙️';
|
|
172
|
-
}
|
|
173
|
-
function getStatusIcon(status) {
|
|
174
|
-
const icons = {
|
|
175
|
-
'completed': '✅',
|
|
176
|
-
'error': '❌',
|
|
177
|
-
'started': '⏳',
|
|
178
|
-
'success': '✅'
|
|
179
|
-
};
|
|
180
|
-
return icons[status] || '📄';
|
|
181
|
-
}
|
|
182
|
-
function getToolDisplayName(toolName) {
|
|
183
|
-
return toolName.replace('bob.', '').replace(/\./g, '.');
|
|
184
|
-
}
|
|
185
|
-
function formatDuration(ms) {
|
|
186
|
-
if (ms < 1000)
|
|
187
|
-
return `${ms}ms`;
|
|
188
|
-
if (ms < 60000)
|
|
189
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
190
|
-
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
|
|
191
|
-
}
|
|
192
|
-
function extractResultSummary(toolName, result) {
|
|
193
|
-
if (!result)
|
|
194
|
-
return 'Completed';
|
|
195
|
-
// Custom summaries based on tool type
|
|
196
|
-
if (toolName.includes('manual')) {
|
|
197
|
-
if (result.spec_id) {
|
|
198
|
-
const action = toolName.includes('create') ? 'Created' : 'Updated';
|
|
199
|
-
return `${action} ${result.spec_id.split('-').pop() || 'manual'}`;
|
|
200
|
-
}
|
|
201
|
-
return 'Manual operation completed';
|
|
202
|
-
}
|
|
203
|
-
if (toolName.includes('search')) {
|
|
204
|
-
if (result.stats?.totalResults !== undefined) {
|
|
205
|
-
const count = result.stats.totalResults;
|
|
206
|
-
return `${count} result${count !== 1 ? 's' : ''} found`;
|
|
207
|
-
}
|
|
208
|
-
if (result.lexicalHits && result.semanticHits) {
|
|
209
|
-
const total = result.lexicalHits.length + result.semanticHits.length;
|
|
210
|
-
return `${total} result${total !== 1 ? 's' : ''} found`;
|
|
211
|
-
}
|
|
212
|
-
return 'Search completed';
|
|
213
|
-
}
|
|
214
|
-
if (toolName.includes('workflow')) {
|
|
215
|
-
if (toolName.includes('start') && result.manual?.spec_id) {
|
|
216
|
-
return `Workflow started for ${result.manual.spec_id.split('-').pop()}`;
|
|
217
|
-
}
|
|
218
|
-
if (toolName.includes('deploy')) {
|
|
219
|
-
return 'Deployment completed';
|
|
220
|
-
}
|
|
221
|
-
return 'Workflow operation completed';
|
|
222
|
-
}
|
|
223
|
-
if (toolName.includes('health')) {
|
|
224
|
-
const status = result.status || result.overall_healthy;
|
|
225
|
-
return status === 'healthy' || status === true ? 'System healthy' : 'Issues detected';
|
|
226
|
-
}
|
|
227
|
-
if (toolName.includes('validate')) {
|
|
228
|
-
if (result.overall_compliance) {
|
|
229
|
-
return `Validation: ${result.overall_compliance}`;
|
|
230
|
-
}
|
|
231
|
-
return 'Validation completed';
|
|
232
|
-
}
|
|
233
|
-
return 'Operation completed';
|
|
234
|
-
}
|
|
235
|
-
async function bootstrap() {
|
|
236
|
-
const server = new McpServer({
|
|
237
|
-
name: "bobs-workshop",
|
|
238
|
-
version: "0.2.0"
|
|
239
|
-
}, {
|
|
240
|
-
capabilities: {
|
|
241
|
-
resources: {},
|
|
242
|
-
prompts: {},
|
|
243
|
-
tools: {},
|
|
244
|
-
logging: {}
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
// Initialize MCP logging with error handling
|
|
248
|
-
try {
|
|
249
|
-
mcpLogger.initialize(server.server);
|
|
250
|
-
console.log("MCP logging initialized successfully");
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
console.error("Failed to initialize MCP logging:", error);
|
|
254
|
-
// Continue without logging rather than failing completely
|
|
255
|
-
}
|
|
256
|
-
// Initialize health checks
|
|
257
|
-
await initializeHealthChecks();
|
|
258
|
-
// Send startup observation to reinforce policy awareness
|
|
259
|
-
try {
|
|
260
|
-
await mcpLogger.info("👋 Welcome to Bob's Workshop! Please review mcp://bobs-workshop/policy before invoking any tools.");
|
|
261
|
-
}
|
|
262
|
-
catch (error) {
|
|
263
|
-
console.log("Note: Could not send startup observation, but continuing normally");
|
|
264
|
-
}
|
|
265
|
-
// Enhanced tool call wrapper with reliability features and modern CLI output
|
|
266
|
-
const withToolLogging = (toolName, handler) => {
|
|
267
|
-
return withErrorHandling(async (args, extra) => {
|
|
268
|
-
const startTime = Date.now();
|
|
269
|
-
const specId = (args && typeof args === 'object' && 'spec_id' in args) ? args.spec_id : undefined;
|
|
270
|
-
// Start logical operation for tool call
|
|
271
|
-
const operationId = await mcpLogger.startLogicalOperation('tool_call', specId, `🔧 Executing ${toolName}`);
|
|
272
|
-
// Generate enhanced status message for Claude Code terminal
|
|
273
|
-
const statusMessage = generateToolStatusMessage(toolName, args, 'started');
|
|
274
|
-
console.log(statusMessage);
|
|
275
|
-
await mcpLogger.logToolCall(toolName, args, specId);
|
|
276
|
-
try {
|
|
277
|
-
// Apply timeout to tool operations (30 seconds for most tools)
|
|
278
|
-
const timeoutMs = getToolTimeout(toolName);
|
|
279
|
-
const result = await withTimeout(() => handler(args, extra), { timeout: timeoutMs });
|
|
280
|
-
const duration = Date.now() - startTime;
|
|
281
|
-
// Add success sub-event
|
|
282
|
-
await mcpLogger.addSubEvent(operationId, {
|
|
283
|
-
timestamp: new Date().toISOString(),
|
|
284
|
-
event: 'tool_success',
|
|
285
|
-
details: { tool_name: toolName, duration_ms: duration }
|
|
286
|
-
});
|
|
287
|
-
// Generate success status message
|
|
288
|
-
const successMessage = generateToolStatusMessage(toolName, args, 'completed', duration, result);
|
|
289
|
-
console.log(successMessage);
|
|
290
|
-
await mcpLogger.logToolSuccess(toolName, duration, specId);
|
|
291
|
-
await mcpLogger.completeLogicalOperation(operationId, true);
|
|
292
|
-
// Return enhanced result with status summary
|
|
293
|
-
return {
|
|
294
|
-
...result,
|
|
295
|
-
_meta: {
|
|
296
|
-
tool: toolName,
|
|
297
|
-
duration_ms: duration,
|
|
298
|
-
status: 'success',
|
|
299
|
-
summary: extractResultSummary(toolName, result)
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
catch (error) {
|
|
304
|
-
const duration = Date.now() - startTime;
|
|
305
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
306
|
-
// Add error sub-event
|
|
307
|
-
await mcpLogger.addSubEvent(operationId, {
|
|
308
|
-
timestamp: new Date().toISOString(),
|
|
309
|
-
event: 'tool_error',
|
|
310
|
-
details: { tool_name: toolName, error: errorMsg }
|
|
311
|
-
});
|
|
312
|
-
// Generate error status message
|
|
313
|
-
const errorMessage = generateToolStatusMessage(toolName, args, 'error', duration, null, errorMsg);
|
|
314
|
-
console.log(errorMessage);
|
|
315
|
-
await mcpLogger.logToolError(toolName, errorMsg, duration, specId);
|
|
316
|
-
await mcpLogger.completeLogicalOperation(operationId, false);
|
|
317
|
-
// Auto-debug if we have a spec_id and this is a critical error
|
|
318
|
-
if (specId && shouldInvokeDebugger(error)) {
|
|
319
|
-
try {
|
|
320
|
-
await autoDebugError({
|
|
321
|
-
spec_id: specId,
|
|
322
|
-
operation: `MCP tool execution: ${toolName}`,
|
|
323
|
-
error: error,
|
|
324
|
-
context: { args, toolName, duration }
|
|
325
|
-
});
|
|
326
|
-
console.log(`[AUTO-DEBUG] Created debug log for ${toolName} error in spec ${specId}`);
|
|
327
|
-
}
|
|
328
|
-
catch (debugError) {
|
|
329
|
-
console.log(`[AUTO-DEBUG] Failed to create debug log: ${debugError}`);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
throw error;
|
|
333
|
-
}
|
|
334
|
-
}, `MCP tool: ${toolName}`, (args) => ({
|
|
335
|
-
tool_name: toolName,
|
|
336
|
-
spec_id: (args && typeof args === 'object' && 'spec_id' in args) ? args.spec_id : undefined
|
|
337
|
-
}));
|
|
338
|
-
};
|
|
339
|
-
// Configure path resolution for manifest (works in both dev and global install)
|
|
340
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
341
|
-
const __dirname = path.dirname(__filename);
|
|
342
|
-
const getManifestPath = () => {
|
|
343
|
-
// Resolve manifest path relative to compiled dist directory
|
|
344
|
-
// When installed globally: /opt/homebrew/lib/node_modules/bobs-mcp/dist/index.js
|
|
345
|
-
// Manifest at: /opt/homebrew/lib/node_modules/bobs-mcp/public/.well-known/mcp/manifest.json
|
|
346
|
-
return path.resolve(__dirname, "../public/.well-known/mcp/manifest.json");
|
|
347
|
-
};
|
|
348
|
-
// Register MCP resource endpoints to expose manifest.json
|
|
349
|
-
// This allows Claude Code to read llm_guidance configuration for tool selection
|
|
350
|
-
server.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
351
|
-
return {
|
|
352
|
-
resources: [
|
|
353
|
-
{
|
|
354
|
-
uri: "mcp://bobs-workshop/manifest",
|
|
355
|
-
name: "Bob's MCP Manifest",
|
|
356
|
-
description: "MCP server manifest with tool definitions and LLM guidance for optimal tool selection",
|
|
357
|
-
mimeType: "application/json"
|
|
358
|
-
},
|
|
359
|
-
{
|
|
360
|
-
uri: "mcp://bobs-workshop/policy",
|
|
361
|
-
name: "Bob's Workshop Policy",
|
|
362
|
-
description: "Runtime behavioral rules and best practices for using Bob's Workshop",
|
|
363
|
-
mimeType: "text/markdown"
|
|
364
|
-
}
|
|
365
|
-
]
|
|
366
|
-
};
|
|
367
|
-
});
|
|
368
|
-
server.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
369
|
-
if (request.params.uri === "mcp://bobs-workshop/manifest") {
|
|
370
|
-
try {
|
|
371
|
-
const manifestPath = getManifestPath();
|
|
372
|
-
// Check if manifest file exists
|
|
373
|
-
if (!await fs.pathExists(manifestPath)) {
|
|
374
|
-
console.error(`[MANIFEST ERROR] Manifest not found at: ${manifestPath}`);
|
|
375
|
-
throw new Error(`Manifest file not found at ${manifestPath}`);
|
|
376
|
-
}
|
|
377
|
-
const manifest = await fs.readFile(manifestPath, "utf-8");
|
|
378
|
-
console.log(`[MANIFEST] Served manifest from ${manifestPath}`);
|
|
379
|
-
return {
|
|
380
|
-
contents: [{
|
|
381
|
-
uri: request.params.uri,
|
|
382
|
-
mimeType: "application/json",
|
|
383
|
-
text: manifest
|
|
384
|
-
}]
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
catch (error) {
|
|
388
|
-
console.error(`[MANIFEST ERROR] Failed to read manifest:`, error);
|
|
389
|
-
throw new Error(`Failed to read manifest: ${error instanceof Error ? error.message : String(error)}`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
if (request.params.uri === "mcp://bobs-workshop/policy") {
|
|
393
|
-
const policyContent = `# Bob's Workshop – Usage Policy
|
|
394
|
-
|
|
395
|
-
## 1. Code Search Best Practices
|
|
396
|
-
|
|
397
|
-
**Understanding Search Modes:**
|
|
398
|
-
|
|
399
|
-
**Lexical Mode** (pattern matching via ripgrep):
|
|
400
|
-
- ✅ Single terms: \`bob.code.search({ query: "mcpLogger", mode: "lexical" })\`
|
|
401
|
-
- ✅ Regex patterns: \`bob.code.search({ query: "mcpLogger\\\\.(log|info)", mode: "lexical" })\`
|
|
402
|
-
- ✅ Multi-word: Automatically interpreted with smart cascade (see below)
|
|
403
|
-
|
|
404
|
-
**Multi-Word Query Smart Cascade:**
|
|
405
|
-
When you use multi-word queries like "pdf export", the tool automatically tries patterns in order:
|
|
406
|
-
1. **Method call** (2 words, identifiers): \`object\\.(method)\`
|
|
407
|
-
2. **Ordered proximity** (3+ words or fallback): \`word1.*word2.*word3\`
|
|
408
|
-
3. **OR pattern** (last resort if #2 returns 0): \`\\b(word1|word2|word3)\\b\`
|
|
409
|
-
|
|
410
|
-
The tool will inform you which pattern was used and return results automatically.
|
|
411
|
-
|
|
412
|
-
**Semantic Mode** (conceptual via semgrep):
|
|
413
|
-
- ✅ Multi-word concepts: \`bob.code.search({ query: "authentication error handling", mode: "auto" })\`
|
|
414
|
-
- Automatically selected for 3+ word queries in architect/debugger/reviewer phases
|
|
415
|
-
- Best for understanding architecture, security, patterns
|
|
416
|
-
|
|
417
|
-
**Auto Mode** (recommended - intelligent routing):
|
|
418
|
-
- Automatically chooses lexical or semantic based on query and phase
|
|
419
|
-
- 3+ words + semantic-enabled phase → semantic mode
|
|
420
|
-
- Otherwise → lexical mode with smart cascade
|
|
421
|
-
|
|
422
|
-
**Examples:**
|
|
423
|
-
\`\`\`javascript
|
|
424
|
-
// Method call (auto-detects)
|
|
425
|
-
bob.code.search({ query: "mcpLogger info", mode: "lexical" })
|
|
426
|
-
// → Tries: mcpLogger\\.(info) → Results found ✅
|
|
427
|
-
|
|
428
|
-
// Concept search (ordered proximity)
|
|
429
|
-
bob.code.search({ query: "pdf export", mode: "lexical" })
|
|
430
|
-
// → Tries: pdf.*export → 355 results ✅
|
|
431
|
-
// → Note: Skipped OR pattern (would return 1,665 noisy results)
|
|
432
|
-
|
|
433
|
-
// Semantic routing (auto mode)
|
|
434
|
-
bob.code.search({ query: "authentication flow security", mode: "auto", phase: "architect" })
|
|
435
|
-
// → Routes to semantic search
|
|
436
|
-
\`\`\`
|
|
437
|
-
|
|
438
|
-
## 2. Tool Preference Hierarchy
|
|
439
|
-
|
|
440
|
-
**Search Strategy:**
|
|
441
|
-
1. **Start with \`bob.code.search\`** (auto mode) - smart cascade handles multi-word
|
|
442
|
-
2. **Use Glob** for finding specific files by name/pattern
|
|
443
|
-
3. **Use Read** when you know the exact file path
|
|
444
|
-
4. **Use Grep** only for exact string matching (when bob.code.search doesn't fit)
|
|
445
|
-
|
|
446
|
-
**Manual Updates:**
|
|
447
|
-
- ❌ DO NOT create separate markdown files
|
|
448
|
-
- ✅ ALWAYS use \`bob.manual.update\` with execution_log/debug_log
|
|
449
|
-
|
|
450
|
-
**Git Operations:**
|
|
451
|
-
- ❌ DO NOT use manual git commands
|
|
452
|
-
- ✅ ALWAYS use \`bob.workflow.start\` and \`bob.workflow.deploy\`
|
|
453
|
-
|
|
454
|
-
## 3. Workflow Sequence
|
|
455
|
-
Follow the mode progression:
|
|
456
|
-
Orchestrator → Architect → Engineer → Debugger → Reviewer
|
|
457
|
-
|
|
458
|
-
## 4. Manual as Single Source of Truth
|
|
459
|
-
- All specifications, logs, and progress MUST be in SPEC files
|
|
460
|
-
- DO NOT create separate markdown files
|
|
461
|
-
- Use \`bob.manual.update\` with execution_log or debug_log
|
|
462
|
-
|
|
463
|
-
## 5. Mode-Specific Responsibilities
|
|
464
|
-
**Architect**: Creates executive_summary, product_specifications, architecture_analysis, implementation_plan, testing
|
|
465
|
-
**Engineer**: Updates execution_logs
|
|
466
|
-
**Debugger**: Updates debug_logs
|
|
467
|
-
**Reviewer**: Creates own review manual
|
|
468
|
-
|
|
469
|
-
## 6. Before Acting
|
|
470
|
-
1. Review this policy
|
|
471
|
-
2. Use \`bob.code.search\` to locate information
|
|
472
|
-
3. Log updates with \`bob.manual.update\`
|
|
473
|
-
`;
|
|
474
|
-
console.log(`[POLICY] Served policy resource`);
|
|
475
|
-
return {
|
|
476
|
-
contents: [{
|
|
477
|
-
uri: request.params.uri,
|
|
478
|
-
mimeType: "text/markdown",
|
|
479
|
-
text: policyContent
|
|
480
|
-
}]
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
throw new Error(`Unknown resource URI: ${request.params.uri}`);
|
|
484
|
-
});
|
|
485
|
-
console.log("[MANIFEST] Resource endpoints registered successfully");
|
|
486
|
-
// Register orchestrator tool (main entry point)
|
|
487
|
-
server.registerTool("bob.workshop", {
|
|
488
|
-
title: "🔧 Intelligent workflow orchestrator for all development tasks",
|
|
489
|
-
description: "🔧 Main entry point for all development workflows. Analyzes problems, asks clarifying questions when needed, and assigns you a role (Architect, Engineer, Debugger, Reviewer). When this tool returns, YOU will assume the assigned role and execute the provided actions immediately. This is not external routing - you are the agent.",
|
|
490
|
-
inputSchema: {
|
|
491
|
-
problem: z.string().min(5).describe("Problem statement or task description (e.g. 'Add JWT authentication', 'Fix login bug', 'Review user service')"),
|
|
492
|
-
mode: z.enum(["architect", "engineer", "debugger", "reviewer"]).optional().describe("Preferred mode (optional - will be auto-determined based on problem)"),
|
|
493
|
-
spec_id: z.string().optional().describe("Continue work on existing manual (optional)"),
|
|
494
|
-
clarifications: z.record(z.string()).optional().describe("Answers to previous clarifying questions")
|
|
495
|
-
}
|
|
496
|
-
}, withToolLogging("bob.workshop", async ({ problem, mode, spec_id, clarifications }) => {
|
|
497
|
-
console.log("bob.workshop orchestrator called with:", { problem, mode, spec_id, clarifications });
|
|
498
|
-
const result = await workshopHandler({ problem, mode, spec_id, clarifications });
|
|
499
|
-
// Format response with role directive if routing to a mode (BACKEND-002 & BACKEND-003)
|
|
500
|
-
// This implements Option A: Enhanced Tool Response pattern from workflows-mcp-server
|
|
501
|
-
let responseText;
|
|
502
|
-
if ((result.action === 'route' || result.action === 'continue') && result.next_mode) {
|
|
503
|
-
const directive = formatRoleDirective(result.next_mode, {
|
|
504
|
-
spec_id: result.spec_id,
|
|
505
|
-
problem: problem,
|
|
506
|
-
confidence: 'confidence_score' in result ? result.confidence_score : undefined
|
|
507
|
-
});
|
|
508
|
-
responseText = `${directive}\n\n---\n\nContext (JSON):\n${JSON.stringify(result, null, 2)}`;
|
|
509
|
-
}
|
|
510
|
-
else {
|
|
511
|
-
// For clarify, await_approval, etc. - just return JSON (no directive)
|
|
512
|
-
responseText = JSON.stringify(result, null, 2);
|
|
513
|
-
}
|
|
514
|
-
return { content: [{ type: "text", text: responseText }] };
|
|
515
|
-
}));
|
|
516
|
-
// Register Manual management tools (new workshop terminology)
|
|
517
|
-
server.registerTool("bob.manual.create", {
|
|
518
|
-
title: "🔧 Start a new feature manual for this project",
|
|
519
|
-
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.",
|
|
520
|
-
inputSchema: {
|
|
521
|
-
title: z.string().describe("Title of the feature/app to build (e.g. 'Sudoku PWA', 'User Authentication')"),
|
|
522
|
-
author: z.string().optional().describe("Author of the manual"),
|
|
523
|
-
category: z.string().optional().describe("Category: frontend, backend, fullstack, devtools, etc."),
|
|
524
|
-
priority: z.string().optional().describe("Priority: low, medium, high, critical"),
|
|
525
|
-
tags: z.array(z.string()).optional().describe("Tags for organization (e.g. ['pwa', 'mobile', 'game'])")
|
|
526
|
-
}
|
|
527
|
-
}, withToolLogging("bob.manual.create", async ({ title, author, category, priority, tags }) => {
|
|
528
|
-
console.log("bob.manual.create MCP tool called with:", { title, author, category, priority, tags });
|
|
529
|
-
const result = await specCreateHandler({ title, author, category, priority, tags, initial_state: "draft" });
|
|
530
|
-
const conciseText = await formatConciseResponse("bob.manual.create", result);
|
|
531
|
-
return { content: [{ type: "text", text: conciseText }] };
|
|
532
|
-
}));
|
|
533
|
-
server.registerTool("bob.manual.update", {
|
|
534
|
-
title: "🔧 Update details or logs in a manual",
|
|
535
|
-
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.",
|
|
536
|
-
inputSchema: {
|
|
537
|
-
spec_id: z.string().describe("Manual ID to update"),
|
|
538
|
-
section: z.enum(["executive_summary", "product_specifications", "architecture_analysis", "implementation_plan", "testing", "risk_assessment", "review"]).optional().describe("Section to update"),
|
|
539
|
-
content: z.string().optional().describe("Content to add to the section"),
|
|
540
|
-
execution_log: z.object({
|
|
541
|
-
timestamp: z.string(),
|
|
542
|
-
engineer: z.string(),
|
|
543
|
-
action: z.string(),
|
|
544
|
-
task_id: z.string(),
|
|
545
|
-
files_changed: z.array(z.string()),
|
|
546
|
-
commit_hash: z.string(),
|
|
547
|
-
note: z.string()
|
|
548
|
-
}).optional().describe("Execution log entry"),
|
|
549
|
-
debug_log: z.object({
|
|
550
|
-
timestamp: z.string(),
|
|
551
|
-
issue: z.string(),
|
|
552
|
-
root_cause: z.string(),
|
|
553
|
-
fix: z.string(),
|
|
554
|
-
confidence: z.string()
|
|
555
|
-
}).optional().describe("Debug log entry"),
|
|
556
|
-
state: z.enum(["draft", "ready", "engineered", "done"]).optional().describe("Manual state to set explicitly")
|
|
557
|
-
}
|
|
558
|
-
}, withToolLogging("bob.manual.update", async ({ spec_id, section, content, execution_log, debug_log, state }) => {
|
|
559
|
-
const result = await specUpdateHandler({ spec_id, section, content, execution_log, debug_log, state });
|
|
560
|
-
// Detect mode based on what type of update this is
|
|
561
|
-
let mode = 'unknown';
|
|
562
|
-
if (execution_log)
|
|
563
|
-
mode = 'engineer';
|
|
564
|
-
else if (debug_log)
|
|
565
|
-
mode = 'debugger';
|
|
566
|
-
else if (section === 'architecture_analysis' || section === 'product_specifications')
|
|
567
|
-
mode = 'architect';
|
|
568
|
-
else if (section === 'review')
|
|
569
|
-
mode = 'reviewer';
|
|
570
|
-
const conciseText = await formatConciseResponse("bob.manual.update", result);
|
|
571
|
-
return { content: [{ type: "text", text: conciseText }] };
|
|
572
|
-
}));
|
|
573
|
-
server.registerTool("bob.manual.get", {
|
|
574
|
-
title: "🔧 Retrieve a specific manual",
|
|
575
|
-
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.",
|
|
576
|
-
inputSchema: {
|
|
577
|
-
spec_id: z.string().describe("Manual ID to retrieve")
|
|
578
|
-
}
|
|
579
|
-
}, withToolLogging("bob.manual.get", async ({ spec_id }) => {
|
|
580
|
-
const result = await specGetHandler({ spec_id });
|
|
581
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
582
|
-
}));
|
|
583
|
-
server.registerTool("bob.manual.list", {
|
|
584
|
-
title: "🔧 List all project manuals",
|
|
585
|
-
description: "🔧 List all project manuals with their current status. Use this to see what features/components are planned, in progress, or completed.",
|
|
586
|
-
inputSchema: {
|
|
587
|
-
filter: z.string().optional().describe("Optional filter by title or category")
|
|
588
|
-
}
|
|
589
|
-
}, async ({ filter }) => {
|
|
590
|
-
const result = await specListHandler({ filter });
|
|
591
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
592
|
-
});
|
|
593
|
-
// REMOVED: Legacy bob.spec.* tools - use bob.manual.* instead
|
|
594
|
-
// REMOVED: Individual worktree and git tools - use bob.workflow.* instead
|
|
595
|
-
// The following tools have been consolidated into bob.workflow.start and bob.workflow.deploy:
|
|
596
|
-
// - bob.worktree.list (use bob.worktree.debug with action: "status" instead)
|
|
597
|
-
// - bob.git.commit (automated by bob.workflow.deploy)
|
|
598
|
-
// - bob.git.merge (automated by bob.workflow.deploy)
|
|
599
|
-
// Advanced worktree maintenance tool (kept for debugging)
|
|
600
|
-
server.registerTool("bob.worktree.debug", {
|
|
601
|
-
title: "🔧 Debug and maintain worktrees",
|
|
602
|
-
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.",
|
|
603
|
-
inputSchema: {
|
|
604
|
-
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"),
|
|
605
|
-
spec_id: z.string().optional().describe("Optional manual ID for validate/repair actions"),
|
|
606
|
-
max_age_days: z.number().optional().default(7).describe("Max age in days for cleanup action (default: 7)")
|
|
607
|
-
}
|
|
608
|
-
}, withToolLogging("bob.worktree.debug", async ({ action, spec_id, max_age_days = 7 }) => {
|
|
609
|
-
const { worktreeDebugHandler } = await import("./tools/worktreeTools.js");
|
|
610
|
-
const result = await worktreeDebugHandler({ action, spec_id, max_age_days });
|
|
611
|
-
const conciseText = await formatConciseResponse("bob.worktree.debug", result);
|
|
612
|
-
return { content: [{ type: "text", text: conciseText }] };
|
|
613
|
-
}));
|
|
614
|
-
// REMOVED: Old search tools - use bob.code.search instead
|
|
615
|
-
server.registerTool("bob.code.search", {
|
|
616
|
-
title: "🔧 Unified lexical + semantic + structural search",
|
|
617
|
-
description: "🔧 Hybrid search combining ripgrep (lexical), semgrep (semantic), and AST-based (structural) for comprehensive code search. Primary search tool with phase-aware execution and normalized outputs.",
|
|
618
|
-
inputSchema: {
|
|
619
|
-
query: z.string().describe("Search query to find in codebase. Structural queries like 'find references to X', 'show dependencies' use AST analysis."),
|
|
620
|
-
mode: z.enum(["lexical", "semantic", "structural", "auto"]).optional().default("auto").describe("Search mode: lexical (ripgrep), semantic (semgrep), structural (AST-based), or auto (intelligent routing)"),
|
|
621
|
-
phase: z.enum(["architect", "engineer", "debugger", "reviewer"]).optional().describe("Caller phase for phase-aware Semgrep rule selection"),
|
|
622
|
-
path: z.string().optional().describe("Root path or subdirectory to search (optional)"),
|
|
623
|
-
includeHidden: z.boolean().optional().default(false).describe("Include hidden/ignored files (equivalent to rg -uuu)"),
|
|
624
|
-
followGitIgnore: z.boolean().optional().default(true).describe("Respect .gitignore rules (default: true)"),
|
|
625
|
-
fileTypes: z.array(z.string()).optional().describe("File types to include (e.g., ['ts', 'tsx', 'py'])"),
|
|
626
|
-
maxHits: z.number().optional().default(30).describe("Maximum total results to return (default: 30 for token efficiency)"),
|
|
627
|
-
perFileLimit: z.number().optional().default(5).describe("Maximum matches per file (default: 5 to prevent single-file domination)"),
|
|
628
|
-
contextLines: z.number().optional().default(2).describe("Context lines before/after matches"),
|
|
629
|
-
timeoutMs: z.number().optional().default(4000).describe("Timeout in milliseconds"),
|
|
630
|
-
verbose: z.boolean().optional().default(false).describe("Include full lexicalHits/semanticHits arrays for debugging (increases token usage)")
|
|
631
|
-
}
|
|
632
|
-
}, async ({ query, mode, phase, path, includeHidden, followGitIgnore, fileTypes, maxHits, perFileLimit, contextLines, timeoutMs, verbose }) => {
|
|
633
|
-
const result = await hybridSearchHandler({
|
|
634
|
-
query,
|
|
635
|
-
mode: mode || "auto",
|
|
636
|
-
phase,
|
|
637
|
-
path,
|
|
638
|
-
includeHidden: includeHidden || false,
|
|
639
|
-
followGitIgnore: followGitIgnore !== false,
|
|
640
|
-
fileTypes,
|
|
641
|
-
maxHits: maxHits || 30,
|
|
642
|
-
perFileLimit: perFileLimit || 5,
|
|
643
|
-
contextLines: contextLines || 2,
|
|
644
|
-
timeoutMs: timeoutMs || 4000,
|
|
645
|
-
verbose: verbose || false
|
|
646
|
-
});
|
|
647
|
-
// Keep search results verbose since Claude needs to see the content
|
|
648
|
-
// But add concise summary to _meta for status messages
|
|
649
|
-
const enhancedResult = {
|
|
650
|
-
...result,
|
|
651
|
-
_meta: {
|
|
652
|
-
...(result._meta || {}),
|
|
653
|
-
summary: formatConciseResponse("bob.code.search", result)
|
|
654
|
-
}
|
|
655
|
-
};
|
|
656
|
-
return {
|
|
657
|
-
content: [{
|
|
658
|
-
type: "text",
|
|
659
|
-
text: JSON.stringify(enhancedResult, null, 2)
|
|
660
|
-
}]
|
|
661
|
-
};
|
|
662
|
-
});
|
|
663
|
-
// Register dashboard tools
|
|
664
|
-
server.registerTool("bob.dashboard.launch", {
|
|
665
|
-
title: "🔧 Open the visual workshop management interface",
|
|
666
|
-
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."
|
|
667
|
-
}, async () => {
|
|
668
|
-
const result = await dashboardLaunchHandler();
|
|
669
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
670
|
-
});
|
|
671
|
-
// REMOVED: bob.dashboard.update tool - use bob.manual.update with execution_log/debug_log instead
|
|
672
|
-
// REMOVED: Approval tools - use structured outputs with approval_required flag instead
|
|
673
|
-
// Register Simplified Workflow tools (bundled git + worktree operations)
|
|
674
|
-
server.registerTool("bob.workflow.start", {
|
|
675
|
-
title: "🔧 Start new feature development workflow",
|
|
676
|
-
description: "🔧 Architect workflow: Create manual + worktree + launch dashboard. Bundles manual creation, git worktree setup, and dashboard launch for streamlined feature development start.",
|
|
677
|
-
inputSchema: {
|
|
678
|
-
title: z.string().describe("Title of the feature/app to build"),
|
|
679
|
-
author: z.string().optional().describe("Author of the manual"),
|
|
680
|
-
category: z.string().optional().describe("Category: frontend, backend, fullstack, devtools, etc."),
|
|
681
|
-
priority: z.string().optional().describe("Priority: low, medium, high, critical"),
|
|
682
|
-
tags: z.array(z.string()).optional().describe("Tags for organization"),
|
|
683
|
-
branch_name: z.string().optional().describe("Custom branch name (auto-generated if not provided)")
|
|
684
|
-
}
|
|
685
|
-
}, withToolLogging("bob.workflow.start", async ({ title, author, category, priority, tags, branch_name }) => {
|
|
686
|
-
console.log("bob.workflow.start called with:", { title, author, category, priority, tags, branch_name });
|
|
687
|
-
// Create manual
|
|
688
|
-
const manual = await specCreateHandler({ title, author, category, priority, tags, initial_state: "draft" });
|
|
689
|
-
// Create worktree with auto-generated branch name if not provided
|
|
690
|
-
const branchName = branch_name || `feature/${title.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '')}`;
|
|
691
|
-
const worktree = await worktreeCreateHandler({ spec_id: manual.spec_id, branch: branchName });
|
|
692
|
-
// Launch dashboard
|
|
693
|
-
const dashboard = await dashboardLaunchHandler();
|
|
694
|
-
// Log workflow initialization via execution log (dashboard reads from SPEC file)
|
|
695
|
-
await specUpdateHandler({
|
|
696
|
-
spec_id: manual.spec_id,
|
|
697
|
-
execution_log: {
|
|
698
|
-
timestamp: new Date().toISOString(),
|
|
699
|
-
engineer: "architect",
|
|
700
|
-
action: "planning: milestone_completed",
|
|
701
|
-
task_id: "WORKFLOW_INIT",
|
|
702
|
-
files_changed: [`manual: ${manual.spec_id}`, `worktree: ${branchName}`, "dashboard: launched"],
|
|
703
|
-
commit_hash: "pending",
|
|
704
|
-
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"
|
|
705
|
-
}
|
|
706
|
-
});
|
|
707
|
-
// NOTE: Dev server startup moved to post-test phase in Engineer workflow
|
|
708
|
-
// This prevents early resource conflicts and aligns with 3-gate approval system
|
|
709
|
-
const workflowResult = {
|
|
710
|
-
manual: manual,
|
|
711
|
-
worktree: worktree,
|
|
712
|
-
dashboard: dashboard,
|
|
713
|
-
workflow_status: "started",
|
|
714
|
-
work_recording: "active",
|
|
715
|
-
note: "Dev server will be started after implementation and testing"
|
|
716
|
-
};
|
|
717
|
-
const conciseText = await formatConciseResponse("bob.workflow.start", workflowResult);
|
|
718
|
-
return {
|
|
719
|
-
content: [{
|
|
720
|
-
type: "text",
|
|
721
|
-
text: conciseText
|
|
722
|
-
}]
|
|
723
|
-
};
|
|
724
|
-
}));
|
|
725
|
-
server.registerTool("bob.workflow.deploy", {
|
|
726
|
-
title: "🔧 Complete implementation and deploy to main",
|
|
727
|
-
description: "🔧 Engineer workflow: Commit changes + merge to main + cleanup worktree. Bundles git operations for streamlined feature completion and deployment.",
|
|
728
|
-
inputSchema: {
|
|
729
|
-
spec_id: z.string().describe("Manual ID for the completed implementation"),
|
|
730
|
-
action_description: z.string().describe("Brief description of what was implemented"),
|
|
731
|
-
files_changed: z.array(z.string()).optional().describe("List of files changed (auto-detected if not provided)"),
|
|
732
|
-
target_branch: z.string().optional().default("main").describe("Target branch to merge into")
|
|
733
|
-
}
|
|
734
|
-
}, withToolLogging("bob.workflow.deploy", async ({ spec_id, action_description, files_changed, target_branch = "main" }) => {
|
|
735
|
-
console.log("bob.workflow.deploy called with:", { spec_id, action_description, files_changed, target_branch });
|
|
736
|
-
// Get git status and auto-detect changed files if not provided
|
|
737
|
-
const gitStatus = await getGitStatus();
|
|
738
|
-
const changedFiles = files_changed || [...gitStatus.staged_files, ...gitStatus.unstaged_files, ...gitStatus.untracked_files];
|
|
739
|
-
// NEW: Stop any running dev server before deployment
|
|
740
|
-
let serverCleanupInfo = null;
|
|
741
|
-
try {
|
|
742
|
-
console.log(`Stopping dev server for manual ${spec_id}...`);
|
|
743
|
-
const serverStopped = await stopDevServer(spec_id);
|
|
744
|
-
if (serverStopped) {
|
|
745
|
-
serverCleanupInfo = { server_stopped: true };
|
|
746
|
-
console.log(`Dev server stopped successfully for manual ${spec_id}`);
|
|
747
|
-
// Log server cleanup
|
|
748
|
-
await specUpdateHandler({
|
|
749
|
-
spec_id,
|
|
750
|
-
execution_log: {
|
|
751
|
-
timestamp: new Date().toISOString(),
|
|
752
|
-
engineer: "system",
|
|
753
|
-
action: "server_stopped",
|
|
754
|
-
task_id: "auto-server-cleanup",
|
|
755
|
-
files_changed: [],
|
|
756
|
-
commit_hash: "pending",
|
|
757
|
-
note: `Auto-stopped dev server before deployment`
|
|
758
|
-
}
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
else {
|
|
762
|
-
serverCleanupInfo = { server_stopped: false, reason: "no_server_running" };
|
|
763
|
-
console.log(`No running server found for manual ${spec_id}`);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
catch (serverError) {
|
|
767
|
-
console.warn('Server cleanup failed (non-critical):', serverError);
|
|
768
|
-
serverCleanupInfo = { server_stopped: false, error: String(serverError) };
|
|
769
|
-
}
|
|
770
|
-
// Find worktree path for this spec
|
|
771
|
-
const worktreePath = await findWorktreeForSpec(spec_id);
|
|
772
|
-
console.log(`Found worktree path: ${worktreePath}`);
|
|
773
|
-
// Commit changes
|
|
774
|
-
const commit = await autoCommitChanges({
|
|
775
|
-
spec_id,
|
|
776
|
-
files_changed: changedFiles,
|
|
777
|
-
action_description,
|
|
778
|
-
worktree_path: worktreePath || undefined
|
|
779
|
-
});
|
|
780
|
-
// Merge and cleanup
|
|
781
|
-
const merge = await autoMergeAndCleanup({
|
|
782
|
-
spec_id,
|
|
783
|
-
target_branch,
|
|
784
|
-
cleanup_worktree: true
|
|
785
|
-
});
|
|
786
|
-
// Record completion milestone (dashboard reads from SPEC file)
|
|
787
|
-
await specUpdateHandler({
|
|
788
|
-
spec_id: spec_id,
|
|
789
|
-
execution_log: {
|
|
790
|
-
timestamp: new Date().toISOString(),
|
|
791
|
-
engineer: "engineer",
|
|
792
|
-
action: "implementation: workflow_deployment_completed",
|
|
793
|
-
task_id: "WORKFLOW_DEPLOY",
|
|
794
|
-
files_changed: changedFiles,
|
|
795
|
-
commit_hash: commit.commit_hash,
|
|
796
|
-
note: `🚀 Successfully deployed implementation: ${action_description}. Committed ${changedFiles.length} files and merged to ${target_branch}. Commit: ${commit.commit_hash}`
|
|
797
|
-
}
|
|
798
|
-
});
|
|
799
|
-
const deployResult = {
|
|
800
|
-
commit: commit,
|
|
801
|
-
merge: merge,
|
|
802
|
-
server_cleanup: serverCleanupInfo,
|
|
803
|
-
workflow_status: "deployed",
|
|
804
|
-
work_recording: "completed"
|
|
805
|
-
};
|
|
806
|
-
const conciseText = await formatConciseResponse("bob.workflow.deploy", deployResult);
|
|
807
|
-
return {
|
|
808
|
-
content: [{
|
|
809
|
-
type: "text",
|
|
810
|
-
text: conciseText
|
|
811
|
-
}]
|
|
812
|
-
};
|
|
813
|
-
}));
|
|
814
|
-
// Health check tool migrated to CLI: use "bobs health"
|
|
815
|
-
// Register change validation tool
|
|
816
|
-
server.registerTool("bob.validate.changes", {
|
|
817
|
-
title: "🔍 Validate implementation changes against manual",
|
|
818
|
-
description: "🔍 Validate recent file changes against manual expectations. Analyzes actual diffs, checks compliance, and provides human-readable summaries of what was implemented.",
|
|
819
|
-
inputSchema: {
|
|
820
|
-
spec_id: z.string().describe("Manual ID to validate changes against")
|
|
821
|
-
}
|
|
822
|
-
}, withToolLogging("bob.validate.changes", async ({ spec_id }) => {
|
|
823
|
-
console.log("bob.validate.changes called with:", { spec_id });
|
|
824
|
-
if (!spec_id) {
|
|
825
|
-
throw new Error("spec_id is required");
|
|
826
|
-
}
|
|
827
|
-
const validationResult = await changeValidator.validateChangesForSpec(spec_id);
|
|
828
|
-
// Update manual with validation results
|
|
829
|
-
await changeValidator.updateSpecWithValidation(validationResult);
|
|
830
|
-
return { content: [{ type: "text", text: JSON.stringify(validationResult, null, 2) }] };
|
|
831
|
-
}));
|
|
832
|
-
// Register implementation summarizer tool
|
|
833
|
-
server.registerTool("bob.summarize.implementation", {
|
|
834
|
-
title: "📊 Generate implementation summary from chokidar observations",
|
|
835
|
-
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.",
|
|
836
|
-
inputSchema: {
|
|
837
|
-
spec_id: z.string().describe("Manual ID to generate implementation summary for"),
|
|
838
|
-
lookback_hours: z.number().optional().default(24).describe("Hours to look back for changes (default: 24)")
|
|
839
|
-
}
|
|
840
|
-
}, withToolLogging("bob.summarize.implementation", async ({ spec_id, lookback_hours = 24 }) => {
|
|
841
|
-
console.log("bob.summarize.implementation called with:", { spec_id, lookback_hours });
|
|
842
|
-
if (!spec_id) {
|
|
843
|
-
throw new Error("spec_id is required");
|
|
844
|
-
}
|
|
845
|
-
const summary = await implementationSummarizer.generateSummaryForSpec(spec_id, lookback_hours);
|
|
846
|
-
// Update manual with summary
|
|
847
|
-
await implementationSummarizer.updateSpecWithSummary(summary);
|
|
848
|
-
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
849
|
-
}));
|
|
850
|
-
// Cleanup tools migrated to CLI: use "bobs cleanup" and "bobs cleanup --stats"
|
|
851
|
-
// Debug tool - no parameters
|
|
852
|
-
server.registerTool("bob.debug", {
|
|
853
|
-
title: "🔧 Debug Bob's Workshop server state",
|
|
854
|
-
description: "🔧 Debug tool with no parameters. Use this to check Bob's Workshop internal state and troubleshoot issues.",
|
|
855
|
-
}, async () => {
|
|
856
|
-
console.log("bob.debug called successfully");
|
|
857
|
-
return {
|
|
858
|
-
content: [
|
|
859
|
-
{
|
|
860
|
-
type: "text",
|
|
861
|
-
text: "Debug tool works!",
|
|
862
|
-
},
|
|
863
|
-
],
|
|
864
|
-
};
|
|
865
|
-
});
|
|
866
|
-
// Register prompts
|
|
867
|
-
server.registerPrompt("Architect", { description: "Requirements analysis and technical planning specialist" }, async () => ({
|
|
868
|
-
messages: [{ role: "user", content: [{ type: "text", text: ARCHITECT_PROMPT }] }]
|
|
869
|
-
}));
|
|
870
|
-
server.registerPrompt("Engineer", { description: "Implementation specialist following engineering best practices" }, async () => ({
|
|
871
|
-
messages: [{ role: "user", content: [{ type: "text", text: ENGINEER_PROMPT }] }]
|
|
872
|
-
}));
|
|
873
|
-
server.registerPrompt("Debugger", { description: "Issue diagnosis and resolution specialist" }, async () => ({
|
|
874
|
-
messages: [{ role: "user", content: [{ type: "text", text: DEBUGGER_PROMPT }] }]
|
|
875
|
-
}));
|
|
876
|
-
server.registerPrompt("Reviewer", { description: "Quality assurance and security audit specialist" }, async () => ({
|
|
877
|
-
messages: [{ role: "user", content: [{ type: "text", text: REVIEWER_PROMPT }] }]
|
|
878
|
-
}));
|
|
879
|
-
server.registerPrompt("help", { description: "Get guidance on using Bob's Workshop effectively" }, async () => {
|
|
880
|
-
const helpContent = `You are connected to Bob's Workshop MCP.
|
|
881
|
-
|
|
882
|
-
Before taking any actions:
|
|
883
|
-
1. Review \`mcp://bobs-workshop/policy\` for behavioral rules
|
|
884
|
-
2. Use \`bob.code.search\` to locate information
|
|
885
|
-
3. Log updates with \`bob.manual.update\`
|
|
886
|
-
4. Follow the workflow sequence: Orchestrator → Architect → Engineer → Debugger → Reviewer
|
|
887
|
-
|
|
888
|
-
## Quick Reference
|
|
889
|
-
|
|
890
|
-
**Multi-Word Search - Smart Cascade:**
|
|
891
|
-
|
|
892
|
-
The tool automatically interprets your query:
|
|
893
|
-
|
|
894
|
-
**Method Calls** (2 words, identifiers):
|
|
895
|
-
\`\`\`javascript
|
|
896
|
-
{ query: "mcpLogger info", mode: "lexical" }
|
|
897
|
-
// → Auto-tries: mcpLogger\\.(info)
|
|
898
|
-
// → Finds: await mcpLogger.info(...) calls
|
|
899
|
-
\`\`\`
|
|
900
|
-
|
|
901
|
-
**Concept Search** (3+ words or fallback):
|
|
902
|
-
\`\`\`javascript
|
|
903
|
-
{ query: "pdf export", mode: "lexical" }
|
|
904
|
-
// → Auto-tries: pdf.*export
|
|
905
|
-
// → Finds: 355 results about PDF export
|
|
906
|
-
// → Skips noisy OR pattern (would be 1,665 results)
|
|
907
|
-
\`\`\`
|
|
908
|
-
|
|
909
|
-
**How It Works:**
|
|
910
|
-
1. Tries method call pattern (if 2 identifiers)
|
|
911
|
-
2. Tries ordered proximity (word1.*word2.*word3)
|
|
912
|
-
3. Falls back to OR only if needed
|
|
913
|
-
|
|
914
|
-
The tool tells you which pattern matched in the results.
|
|
915
|
-
|
|
916
|
-
**Tool Preference:**
|
|
917
|
-
- ✅ bob.code.search (handles multi-word automatically)
|
|
918
|
-
- ❌ Grep, Glob (for general search)
|
|
919
|
-
|
|
920
|
-
Need more details? Read the full policy at \`mcp://bobs-workshop/policy\`.`;
|
|
921
|
-
return {
|
|
922
|
-
messages: [{ role: "user", content: [{ type: "text", text: helpContent }] }]
|
|
923
|
-
};
|
|
924
|
-
});
|
|
925
|
-
// Start auto-cleanup service
|
|
926
|
-
cleanupService.startAutoCleanup(30); // Every 30 minutes
|
|
927
|
-
console.log("Auto-cleanup service started (30-minute intervals)");
|
|
928
|
-
// Set up graceful shutdown handlers
|
|
929
|
-
const signals = ['SIGTERM', 'SIGINT', 'SIGUSR2'];
|
|
930
|
-
signals.forEach(signal => {
|
|
931
|
-
process.on(signal, async () => {
|
|
932
|
-
console.log(`Received ${signal}, initiating graceful shutdown`);
|
|
933
|
-
await gracefulShutdown(server);
|
|
934
|
-
process.exit(0);
|
|
935
|
-
});
|
|
936
|
-
});
|
|
937
|
-
// Handle uncaught exceptions and rejections
|
|
938
|
-
process.on('uncaughtException', async (error) => {
|
|
939
|
-
console.error('Uncaught Exception:', error);
|
|
940
|
-
globalErrorCollector.add(await import('./utils/errorHandling.js').then(m => m.enhanceError(error, m.createErrorContext('uncaught_exception'))));
|
|
941
|
-
await gracefulShutdown(server);
|
|
942
|
-
process.exit(1);
|
|
943
|
-
});
|
|
944
|
-
process.on('unhandledRejection', async (reason, promise) => {
|
|
945
|
-
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
946
|
-
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
947
|
-
globalErrorCollector.add(await import('./utils/errorHandling.js').then(m => m.enhanceError(error, m.createErrorContext('unhandled_rejection'))));
|
|
948
|
-
await gracefulShutdown(server);
|
|
949
|
-
process.exit(1);
|
|
950
|
-
});
|
|
951
|
-
console.log("Starting Bob MCP server with enhanced reliability...");
|
|
952
|
-
const transport = new StdioServerTransport();
|
|
953
|
-
await server.connect(transport);
|
|
954
|
-
console.log("Bob MCP server started successfully");
|
|
955
|
-
}
|
|
956
|
-
bootstrap().catch(err => {
|
|
957
|
-
console.error("Failed to start Bob MCP:", err);
|
|
958
|
-
process.exit(1);
|
|
959
|
-
});
|
|
960
|
-
//# sourceMappingURL=index.js.map
|