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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/bin/bobs-mcp.js +130 -0
  4. package/dist/api/taskLogger.js +106 -0
  5. package/dist/api/taskLogger.js.map +1 -0
  6. package/dist/cli/checker.js +401 -0
  7. package/dist/cli/checker.js.map +1 -0
  8. package/dist/cli/cleanup.js +131 -0
  9. package/dist/cli/cleanup.js.map +1 -0
  10. package/dist/cli/debug.js +157 -0
  11. package/dist/cli/debug.js.map +1 -0
  12. package/dist/cli/health.js +97 -0
  13. package/dist/cli/health.js.map +1 -0
  14. package/dist/cli/setup.js +81 -0
  15. package/dist/cli/setup.js.map +1 -0
  16. package/dist/cli/workshop.js +42 -0
  17. package/dist/cli/workshop.js.map +1 -0
  18. package/dist/dashboard/server.js +1206 -0
  19. package/dist/dashboard/server.js.map +1 -0
  20. package/dist/index.js +757 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/prompts/architect.js +157 -0
  23. package/dist/prompts/architect.js.map +1 -0
  24. package/dist/prompts/debugger.js +201 -0
  25. package/dist/prompts/debugger.js.map +1 -0
  26. package/dist/prompts/engineer.js +171 -0
  27. package/dist/prompts/engineer.js.map +1 -0
  28. package/dist/prompts/orchestrator.js +225 -0
  29. package/dist/prompts/orchestrator.js.map +1 -0
  30. package/dist/prompts/reviewer.js +199 -0
  31. package/dist/prompts/reviewer.js.map +1 -0
  32. package/dist/services/activitySummarizer.js +353 -0
  33. package/dist/services/activitySummarizer.js.map +1 -0
  34. package/dist/services/changeValidator.js +396 -0
  35. package/dist/services/changeValidator.js.map +1 -0
  36. package/dist/services/claudeOrchestrator.js +343 -0
  37. package/dist/services/claudeOrchestrator.js.map +1 -0
  38. package/dist/services/fileMonitor.js +250 -0
  39. package/dist/services/fileMonitor.js.map +1 -0
  40. package/dist/services/implementationSummarizer.js +306 -0
  41. package/dist/services/implementationSummarizer.js.map +1 -0
  42. package/dist/services/liveMonitor.js +315 -0
  43. package/dist/services/liveMonitor.js.map +1 -0
  44. package/dist/services/mcpAuditLogger.js +104 -0
  45. package/dist/services/mcpAuditLogger.js.map +1 -0
  46. package/dist/services/mcpLogger.js +223 -0
  47. package/dist/services/mcpLogger.js.map +1 -0
  48. package/dist/services/tmuxManager.js +541 -0
  49. package/dist/services/tmuxManager.js.map +1 -0
  50. package/dist/tools/approvalTools.js +244 -0
  51. package/dist/tools/approvalTools.js.map +1 -0
  52. package/dist/tools/autoDebugger.js +147 -0
  53. package/dist/tools/autoDebugger.js.map +1 -0
  54. package/dist/tools/cleanupService.js +221 -0
  55. package/dist/tools/cleanupService.js.map +1 -0
  56. package/dist/tools/dashboardTools.js +359 -0
  57. package/dist/tools/dashboardTools.js.map +1 -0
  58. package/dist/tools/developmentNudges.js +336 -0
  59. package/dist/tools/developmentNudges.js.map +1 -0
  60. package/dist/tools/gitTools.js +741 -0
  61. package/dist/tools/gitTools.js.map +1 -0
  62. package/dist/tools/orchestratorTools.js +765 -0
  63. package/dist/tools/orchestratorTools.js.map +1 -0
  64. package/dist/tools/searchTools.js +788 -0
  65. package/dist/tools/searchTools.js.map +1 -0
  66. package/dist/tools/specTools.js +350 -0
  67. package/dist/tools/specTools.js.map +1 -0
  68. package/dist/tools/tmuxTools.js +100 -0
  69. package/dist/tools/tmuxTools.js.map +1 -0
  70. package/dist/tools/workRecorder.js +215 -0
  71. package/dist/tools/workRecorder.js.map +1 -0
  72. package/dist/tools/worktreeTools.js +705 -0
  73. package/dist/tools/worktreeTools.js.map +1 -0
  74. package/dist/utils/__tests__/integration.test.js +57 -0
  75. package/dist/utils/__tests__/integration.test.js.map +1 -0
  76. package/dist/utils/__tests__/serverDetection.test.js +151 -0
  77. package/dist/utils/__tests__/serverDetection.test.js.map +1 -0
  78. package/dist/utils/errorHandling.js +336 -0
  79. package/dist/utils/errorHandling.js.map +1 -0
  80. package/dist/utils/processManager.js +172 -0
  81. package/dist/utils/processManager.js.map +1 -0
  82. package/dist/utils/reliability.js +263 -0
  83. package/dist/utils/reliability.js.map +1 -0
  84. package/dist/utils/responseFormatter.js +250 -0
  85. package/dist/utils/responseFormatter.js.map +1 -0
  86. package/dist/utils/serverDetection.js +133 -0
  87. package/dist/utils/serverDetection.js.map +1 -0
  88. package/dist/utils/specMigration.js +105 -0
  89. package/dist/utils/specMigration.js.map +1 -0
  90. package/dist/validation/schemas.js +299 -0
  91. package/dist/validation/schemas.js.map +1 -0
  92. package/package.json +79 -0
  93. package/scripts/init-workspace.js +63 -0
  94. package/scripts/install-search-tools.js +116 -0
@@ -0,0 +1,765 @@
1
+ import { z } from "zod";
2
+ import { specCreateHandler, specGetHandler, specListHandler } from "./specTools.js";
3
+ import { dashboardLaunchHandler, dashboardUpdateHandler } from "./dashboardTools.js";
4
+ import { mcpLogger } from "../services/mcpLogger.js";
5
+ import { validateGate1, validateGate2, validateGate3, validateEnhancedStateTransition } from "../validation/schemas.js";
6
+ export const WorkshopInput = z.object({
7
+ problem: z.string().describe("Problem statement or task description"),
8
+ mode: z.enum(["architect", "engineer", "debugger", "reviewer"]).optional().describe("Preferred mode (optional - will be auto-determined)"),
9
+ spec_id: z.string().optional().describe("Continue existing SPEC (optional)"),
10
+ clarifications: z.record(z.string()).optional().describe("Answers to previous clarifying questions"),
11
+ user_approval: z.boolean().optional().describe("User approval for spec or implementation"),
12
+ user_satisfaction: z.boolean().optional().describe("User satisfaction with completed work")
13
+ });
14
+ export const WorkshopOutput = z.object({
15
+ spec_id: z.string(),
16
+ action: z.enum(["clarify", "route", "continue", "await_approval", "proceed", "complete"]),
17
+ clarify_questions: z.array(z.string()).optional(),
18
+ next_mode: z.string().optional(),
19
+ dashboard_url: z.string().optional(),
20
+ prompt_context: z.string().optional(),
21
+ workflow_step: z.enum([
22
+ "questioning", "spec_review", "approved", "implementation",
23
+ "user_review", "completed", "routed_to_architect", "routed_to_engineer",
24
+ "routed_to_debugger", "routed_to_reviewer"
25
+ ]).optional(),
26
+ requires_user_input: z.boolean().optional(),
27
+ approval_message: z.string().optional(),
28
+ confidence_score: z.number().optional().describe("Confidence level in problem understanding (0-1)")
29
+ });
30
+ export async function workshopHandler(input) {
31
+ console.log("bob.workshop orchestrator called with:", input);
32
+ const validated = WorkshopInput.parse(input);
33
+ // Step 1: Analyze the problem statement
34
+ const analysis = analyzeProblem(validated.problem, validated.clarifications);
35
+ // Step 2: Handle existing SPEC continuation
36
+ if (validated.spec_id) {
37
+ return await continueExistingSpec(validated.spec_id, analysis, validated.mode);
38
+ }
39
+ // Step 3: Check for existing related SPECs
40
+ const existingSpecs = await findRelatedSpecs(validated.problem);
41
+ // Step 4: Decide if we need clarification (enhanced threshold and logic)
42
+ if (analysis.confidence < 0.95 && !validated.clarifications && analysis.questions.length > 0) {
43
+ return {
44
+ spec_id: "",
45
+ action: "clarify",
46
+ clarify_questions: analysis.questions,
47
+ next_mode: analysis.suggestedMode,
48
+ dashboard_url: undefined,
49
+ prompt_context: undefined,
50
+ workflow_step: "questioning",
51
+ requires_user_input: true,
52
+ approval_message: `I need to ask ${analysis.questions.length} clarifying question${analysis.questions.length > 1 ? 's' : ''} before proceeding. Please provide answers to ensure accurate implementation.`,
53
+ confidence_score: analysis.confidence
54
+ };
55
+ }
56
+ // Step 5: Create new SPEC for new features with parallel dashboard update
57
+ let specId = "";
58
+ if (analysis.category === "feature" || analysis.category === "research") {
59
+ // Create SPEC and send dashboard update in parallel
60
+ const [newSpec] = await Promise.allSettled([
61
+ specCreateHandler({
62
+ title: extractTitle(validated.problem),
63
+ author: "AI Orchestrator",
64
+ category: mapCategoryToSpecCategory(analysis.category),
65
+ priority: mapComplexityToPriority(analysis.complexity),
66
+ tags: extractTags(validated.problem),
67
+ initial_state: "draft"
68
+ }),
69
+ sendDashboardUpdate("", "orchestrator_started", {
70
+ action: "spec_creation",
71
+ problem: validated.problem,
72
+ analysis: {
73
+ category: analysis.category,
74
+ complexity: analysis.complexity,
75
+ confidence: analysis.confidence
76
+ }
77
+ })
78
+ ]);
79
+ if (newSpec.status === 'fulfilled') {
80
+ specId = newSpec.value.spec_id;
81
+ // Send spec creation notification
82
+ await sendDashboardUpdate(specId, "spec_created", {
83
+ title: extractTitle(validated.problem),
84
+ category: analysis.category,
85
+ complexity: analysis.complexity
86
+ });
87
+ }
88
+ else {
89
+ throw new Error(`Failed to create SPEC: ${newSpec.reason}`);
90
+ }
91
+ }
92
+ else if (existingSpecs.length > 0) {
93
+ // Use most recent existing SPEC for non-feature work
94
+ specId = existingSpecs[0].spec_id;
95
+ // Notify about continuing existing SPEC
96
+ await sendDashboardUpdate(specId, "spec_continued", {
97
+ problem: validated.problem,
98
+ analysis: {
99
+ category: analysis.category,
100
+ complexity: analysis.complexity
101
+ }
102
+ });
103
+ }
104
+ // Step 6: Launch dashboard for new feature work with parallel notification
105
+ let dashboardUrl = "";
106
+ if (analysis.category === "feature" && analysis.suggestedMode === "architect") {
107
+ const [dashboardResult] = await Promise.allSettled([
108
+ dashboardLaunchHandler(),
109
+ sendDashboardUpdate(specId, "dashboard_launch_requested", {
110
+ mode: analysis.suggestedMode,
111
+ reason: "new feature development"
112
+ })
113
+ ]);
114
+ if (dashboardResult.status === 'fulfilled') {
115
+ dashboardUrl = "http://localhost:4577";
116
+ // Notify dashboard is ready
117
+ await sendDashboardUpdate(specId, "dashboard_launched", {
118
+ url: dashboardUrl,
119
+ status: dashboardResult.value?.status || "launched"
120
+ });
121
+ }
122
+ }
123
+ // Step 7: Determine final mode (respect explicit preference)
124
+ const finalMode = validated.mode || analysis.suggestedMode;
125
+ // Log role transition from orchestrator to target mode
126
+ await mcpLogger.logRoleTransition('orchestrator', finalMode, specId, `Problem classified as ${analysis.category} (confidence: ${Math.round(analysis.confidence * 100)}%)`);
127
+ // Step 8: Create context for the target mode
128
+ const promptContext = await createModeContext(finalMode, validated.problem, specId, analysis);
129
+ // Step 9: Send final routing notification in parallel with return
130
+ const result = {
131
+ spec_id: specId,
132
+ action: "route",
133
+ clarify_questions: undefined,
134
+ next_mode: finalMode,
135
+ dashboard_url: dashboardUrl || undefined,
136
+ prompt_context: promptContext,
137
+ workflow_step: `routed_to_${finalMode}`,
138
+ requires_user_input: false,
139
+ approval_message: undefined,
140
+ confidence_score: analysis.confidence
141
+ };
142
+ // Send routing notification without blocking return
143
+ sendDashboardUpdate(specId, "orchestrator_routing", {
144
+ target_mode: finalMode,
145
+ workflow_step: `routed_to_${finalMode}`,
146
+ dashboard_available: !!dashboardUrl
147
+ }).catch(error => {
148
+ console.log('Dashboard routing notification failed (non-blocking):', error);
149
+ });
150
+ return result;
151
+ }
152
+ function analyzeProblem(problem, clarifications) {
153
+ const lowerProblem = problem.toLowerCase();
154
+ // Simple keyword-based analysis (can be enhanced with more sophisticated NLP)
155
+ let category = "feature";
156
+ let suggestedMode = "architect";
157
+ let confidence = 0.8; // Base confidence
158
+ const questions = [];
159
+ // Detect category and mode with priority order (most specific first)
160
+ if (lowerProblem.includes("review") || lowerProblem.includes("audit") || lowerProblem.includes("check") || lowerProblem.includes("vulnerabilities")) {
161
+ category = "review";
162
+ suggestedMode = "reviewer";
163
+ confidence += 0.2;
164
+ }
165
+ else if (lowerProblem.includes("bug") || lowerProblem.includes("fix") || lowerProblem.includes("error") || lowerProblem.includes("broken")) {
166
+ category = "bug";
167
+ suggestedMode = "debugger";
168
+ confidence += 0.2;
169
+ }
170
+ else if (lowerProblem.includes("implement") || lowerProblem.includes("develop") || (lowerProblem.includes("according") && lowerProblem.includes("spec"))) {
171
+ category = "implementation";
172
+ suggestedMode = "engineer";
173
+ confidence += 0.2;
174
+ }
175
+ else if (lowerProblem.includes("add") || lowerProblem.includes("create") || lowerProblem.includes("build") || lowerProblem.includes("new") || lowerProblem.includes("dashboard") || lowerProblem.includes("feature") || lowerProblem.includes("library") || lowerProblem.includes("make me")) {
176
+ category = "feature";
177
+ suggestedMode = "architect";
178
+ confidence += 0.15;
179
+ }
180
+ // Assess complexity
181
+ let complexity = "medium";
182
+ if (lowerProblem.length < 50 || lowerProblem.split(" ").length < 8) {
183
+ complexity = "simple";
184
+ confidence -= 0.1;
185
+ }
186
+ else if (lowerProblem.length > 200 || lowerProblem.includes("system") || lowerProblem.includes("architecture")) {
187
+ complexity = "complex";
188
+ confidence += 0.05;
189
+ }
190
+ // Final category adjustment based on strong indicators (only for ambiguous cases)
191
+ if (category === "feature" && (lowerProblem.includes("dashboard") || lowerProblem.includes("comprehensive") || lowerProblem.includes("components"))) {
192
+ confidence = 0.9; // High confidence for clear feature descriptions
193
+ }
194
+ // Enhanced question generation with more sophisticated probing
195
+ if (!clarifications) {
196
+ // Feature category questions
197
+ if (category === "feature") {
198
+ // Core architecture questions
199
+ if (!lowerProblem.includes("user") && !lowerProblem.includes("api") && !lowerProblem.includes("frontend") && !lowerProblem.includes("backend")) {
200
+ questions.push("Is this a user-facing feature, internal API, or full-stack component?");
201
+ confidence -= 0.08;
202
+ }
203
+ // Data persistence questions
204
+ if (!lowerProblem.includes("database") && !lowerProblem.includes("storage") && !lowerProblem.includes("persist") && complexity !== "simple") {
205
+ questions.push("Does this feature need to store/persist data? If so, what type of data?");
206
+ confidence -= 0.06;
207
+ }
208
+ // Security and access control
209
+ if (!lowerProblem.includes("authentication") && !lowerProblem.includes("permission") && !lowerProblem.includes("role") && lowerProblem.includes("user")) {
210
+ questions.push("What are the authentication and authorization requirements? Who can access this feature?");
211
+ confidence -= 0.06;
212
+ }
213
+ // Integration and dependencies
214
+ if (!lowerProblem.includes("integrate") && !lowerProblem.includes("external") && !lowerProblem.includes("api") && complexity === "complex") {
215
+ questions.push("Does this feature integrate with external services or existing systems?");
216
+ confidence -= 0.04;
217
+ }
218
+ // Performance and scale
219
+ if (!lowerProblem.includes("performance") && !lowerProblem.includes("scale") && !lowerProblem.includes("users") && complexity !== "simple") {
220
+ questions.push("What are the expected performance requirements and user scale?");
221
+ confidence -= 0.04;
222
+ }
223
+ // UI/UX considerations
224
+ if (lowerProblem.includes("user") && !lowerProblem.includes("interface") && !lowerProblem.includes("ui") && !lowerProblem.includes("design")) {
225
+ questions.push("Do you have specific UI/UX requirements or design preferences?");
226
+ confidence -= 0.03;
227
+ }
228
+ // Testing strategy
229
+ if (!lowerProblem.includes("test") && !lowerProblem.includes("quality") && complexity !== "simple") {
230
+ questions.push("What level of testing is required (unit, integration, e2e)?");
231
+ confidence -= 0.03;
232
+ }
233
+ }
234
+ else if (category === "bug") {
235
+ // Reproduction steps
236
+ if (!lowerProblem.includes("reproduce") && !lowerProblem.includes("steps") && !lowerProblem.includes("when")) {
237
+ questions.push("Can you provide detailed steps to reproduce this issue?");
238
+ confidence -= 0.08;
239
+ }
240
+ // Expected vs actual behavior
241
+ if (!lowerProblem.includes("expect") && !lowerProblem.includes("should") && !lowerProblem.includes("supposed")) {
242
+ questions.push("What is the expected behavior vs. what you're actually seeing?");
243
+ confidence -= 0.07;
244
+ }
245
+ // Environment and context
246
+ if (!lowerProblem.includes("environment") && !lowerProblem.includes("browser") && !lowerProblem.includes("version")) {
247
+ questions.push("In what environment does this occur (browser, OS, version, etc.)?");
248
+ confidence -= 0.06;
249
+ }
250
+ // Frequency and impact
251
+ if (!lowerProblem.includes("always") && !lowerProblem.includes("sometimes") && !lowerProblem.includes("critical")) {
252
+ questions.push("How frequently does this occur and what's the business impact?");
253
+ confidence -= 0.05;
254
+ }
255
+ // Error messages and logs
256
+ if (!lowerProblem.includes("error") && !lowerProblem.includes("log") && !lowerProblem.includes("message")) {
257
+ questions.push("Are there any error messages, console logs, or stack traces?");
258
+ confidence -= 0.05;
259
+ }
260
+ }
261
+ else if (category === "implementation") {
262
+ // Existing specifications
263
+ if (!lowerProblem.includes("spec") && !lowerProblem.includes("requirement") && !lowerProblem.includes("design")) {
264
+ questions.push("Do you have existing specifications, designs, or requirements documents?");
265
+ confidence -= 0.07;
266
+ }
267
+ // Technical constraints
268
+ if (!lowerProblem.includes("constraint") && !lowerProblem.includes("limitation") && !lowerProblem.includes("technology")) {
269
+ questions.push("Are there any technical constraints or preferred technologies to use?");
270
+ confidence -= 0.06;
271
+ }
272
+ // Timeline and priority
273
+ if (!lowerProblem.includes("deadline") && !lowerProblem.includes("urgent") && !lowerProblem.includes("priority")) {
274
+ questions.push("What's the timeline and priority level for this implementation?");
275
+ confidence -= 0.04;
276
+ }
277
+ }
278
+ else if (category === "review") {
279
+ // Scope of review
280
+ if (!lowerProblem.includes("component") && !lowerProblem.includes("module") && !lowerProblem.includes("file")) {
281
+ questions.push("What specific components, modules, or files should be reviewed?");
282
+ confidence -= 0.08;
283
+ }
284
+ // Review focus areas
285
+ if (!lowerProblem.includes("security") && !lowerProblem.includes("performance") && !lowerProblem.includes("quality")) {
286
+ questions.push("What should the review focus on: security, performance, code quality, or all?");
287
+ confidence -= 0.06;
288
+ }
289
+ // Standards and guidelines
290
+ if (!lowerProblem.includes("standard") && !lowerProblem.includes("guideline") && !lowerProblem.includes("compliance")) {
291
+ questions.push("Are there specific coding standards or compliance requirements to check against?");
292
+ confidence -= 0.05;
293
+ }
294
+ }
295
+ else if (category === "research") {
296
+ // Research scope
297
+ if (!lowerProblem.includes("scope") && !lowerProblem.includes("depth") && !lowerProblem.includes("focus")) {
298
+ questions.push("What's the scope and depth of research needed? Any specific focus areas?");
299
+ confidence -= 0.07;
300
+ }
301
+ // Deliverables
302
+ if (!lowerProblem.includes("report") && !lowerProblem.includes("document") && !lowerProblem.includes("summary")) {
303
+ questions.push("What deliverables are expected: summary, detailed report, recommendations?");
304
+ confidence -= 0.06;
305
+ }
306
+ }
307
+ // Universal questions for ambiguous or short descriptions
308
+ if (problem.length < 30) {
309
+ questions.push("Could you provide more details about what you're trying to accomplish?");
310
+ confidence -= 0.1;
311
+ }
312
+ if (complexity === "simple" && questions.length === 0 && confidence < 0.9) {
313
+ questions.push("Are there any specific requirements or constraints I should be aware of?");
314
+ confidence -= 0.05;
315
+ }
316
+ // Limit to maximum 3 most important questions to avoid overwhelming user
317
+ if (questions.length > 3) {
318
+ questions.splice(3);
319
+ }
320
+ }
321
+ return {
322
+ confidence: Math.max(0.5, Math.min(1.0, confidence)),
323
+ category,
324
+ complexity,
325
+ suggestedMode,
326
+ questions
327
+ };
328
+ }
329
+ async function continueExistingSpec(specId, analysis, preferredMode) {
330
+ try {
331
+ const spec = await specGetHandler({ spec_id: specId });
332
+ const mode = preferredMode || analysis.suggestedMode;
333
+ // Log role transition for existing spec continuation
334
+ await mcpLogger.logRoleTransition('orchestrator', mode, specId, `Continuing existing SPEC work, transitioning to ${mode} mode`);
335
+ const promptContext = await createModeContext(mode, "", specId, analysis);
336
+ // Send dashboard notification about continuing existing SPEC
337
+ sendDashboardUpdate(specId, "spec_continuation", {
338
+ mode,
339
+ analysis: {
340
+ category: analysis.category,
341
+ complexity: analysis.complexity,
342
+ confidence: analysis.confidence
343
+ },
344
+ spec_title: spec.title
345
+ }).catch(error => {
346
+ console.log('Dashboard continuation notification failed (non-blocking):', error);
347
+ });
348
+ return {
349
+ spec_id: specId,
350
+ action: "continue",
351
+ clarify_questions: undefined,
352
+ next_mode: mode,
353
+ dashboard_url: undefined,
354
+ prompt_context: promptContext,
355
+ workflow_step: `continuing_with_${mode}`
356
+ };
357
+ }
358
+ catch (error) {
359
+ throw new Error(`Cannot continue SPEC ${specId}: ${error}`);
360
+ }
361
+ }
362
+ async function findRelatedSpecs(problem) {
363
+ try {
364
+ const allSpecs = await specListHandler({});
365
+ const keywords = extractKeywords(problem);
366
+ return allSpecs.specs.filter((spec) => {
367
+ const specText = (spec.title + " " + spec.category).toLowerCase();
368
+ return keywords.some(keyword => specText.includes(keyword));
369
+ }).slice(0, 3); // Return top 3 matches
370
+ }
371
+ catch (error) {
372
+ return [];
373
+ }
374
+ }
375
+ function extractTitle(problem) {
376
+ // Extract a concise title from the problem statement
377
+ const words = problem.split(" ").slice(0, 8);
378
+ return words.join(" ").replace(/[^\w\s-]/g, "").trim();
379
+ }
380
+ function extractKeywords(text) {
381
+ const stopWords = ["the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with", "by"];
382
+ return text.toLowerCase()
383
+ .split(/\s+/)
384
+ .filter(word => word.length > 2 && !stopWords.includes(word))
385
+ .slice(0, 5);
386
+ }
387
+ function extractTags(problem) {
388
+ const lowerProblem = problem.toLowerCase();
389
+ const tags = [];
390
+ // Common technology/domain tags
391
+ const tagMap = {
392
+ "react": ["frontend", "react"],
393
+ "api": ["backend", "api"],
394
+ "database": ["backend", "database"],
395
+ "auth": ["security", "authentication"],
396
+ "user": ["frontend", "ux"],
397
+ "mobile": ["mobile", "responsive"],
398
+ "test": ["testing", "qa"],
399
+ "performance": ["performance", "optimization"]
400
+ };
401
+ Object.entries(tagMap).forEach(([keyword, tagList]) => {
402
+ if (lowerProblem.includes(keyword)) {
403
+ tags.push(...tagList);
404
+ }
405
+ });
406
+ return [...new Set(tags)]; // Remove duplicates
407
+ }
408
+ function mapCategoryToSpecCategory(category) {
409
+ const mapping = {
410
+ "feature": "fullstack",
411
+ "bug": "general",
412
+ "review": "general",
413
+ "implementation": "backend",
414
+ "research": "general"
415
+ };
416
+ return mapping[category];
417
+ }
418
+ function mapComplexityToPriority(complexity) {
419
+ const mapping = {
420
+ "simple": "low",
421
+ "medium": "medium",
422
+ "complex": "high"
423
+ };
424
+ return mapping[complexity];
425
+ }
426
+ async function createModeContext(mode, problem, specId, analysis) {
427
+ let context = `ORCHESTRATOR CONTEXT:\n`;
428
+ context += `- Mode: ${mode}\n`;
429
+ context += `- Problem: ${problem}\n`;
430
+ context += `- SPEC ID: ${specId}\n`;
431
+ context += `- Category: ${analysis.category}\n`;
432
+ context += `- Complexity: ${analysis.complexity}\n`;
433
+ context += `- Confidence: ${(analysis.confidence * 100).toFixed(1)}%\n\n`;
434
+ // Add mode-specific guidance
435
+ switch (mode) {
436
+ case "architect":
437
+ context += `ARCHITECT GUIDANCE:\n`;
438
+ context += `- Create comprehensive SPEC with all required sections\n`;
439
+ context += `- Research existing codebase patterns first\n`;
440
+ context += `- Launch dashboard for visual tracking\n`;
441
+ context += `- Focus on technical architecture and implementation plan\n`;
442
+ break;
443
+ case "engineer":
444
+ context += `ENGINEER GUIDANCE:\n`;
445
+ context += `- Load existing SPEC for implementation guidance\n`;
446
+ context += `- Create worktree for isolated development\n`;
447
+ context += `- Follow implementation plan from SPEC\n`;
448
+ context += `- Log progress in execution logs\n`;
449
+ break;
450
+ case "debugger":
451
+ context += `DEBUGGER GUIDANCE:\n`;
452
+ context += `- Analyze issue systematically\n`;
453
+ context += `- Research existing codebase for similar patterns\n`;
454
+ context += `- Apply minimal, safe fixes\n`;
455
+ context += `- Document findings in debug logs\n`;
456
+ break;
457
+ case "reviewer":
458
+ context += `REVIEWER GUIDANCE:\n`;
459
+ context += `- Focus on security, performance, and quality\n`;
460
+ context += `- Provide severity-based findings\n`;
461
+ context += `- Check for common vulnerabilities\n`;
462
+ context += `- Validate against project standards\n`;
463
+ break;
464
+ }
465
+ return context;
466
+ }
467
+ // Helper function to send dashboard updates without blocking main workflow
468
+ async function sendDashboardUpdate(specId, event, data) {
469
+ try {
470
+ await dashboardUpdateHandler({
471
+ spec_id: specId,
472
+ event,
473
+ data
474
+ });
475
+ }
476
+ catch (error) {
477
+ // Don't let dashboard update failures break the main workflow
478
+ console.log(`Dashboard update failed (non-blocking): [${specId}] ${event}`, error instanceof Error ? error.message : String(error));
479
+ }
480
+ }
481
+ // Search enforcement functions for Debugger
482
+ function countSearchToolUsage(specId) {
483
+ let searchCount = 0;
484
+ // Check active operations for search tool calls
485
+ const activeOps = mcpLogger.getActiveOperationsForSpec(specId);
486
+ for (const operationId of activeOps) {
487
+ const operation = mcpLogger.activeOperations.get(operationId);
488
+ if (operation && operation.details.tool_name === 'bob.code.search') {
489
+ searchCount++;
490
+ }
491
+ }
492
+ // Also check completed tool calls in the log buffer (for recent completions)
493
+ const logBuffer = mcpLogger.logBuffer || [];
494
+ for (const entry of logBuffer) {
495
+ if (entry.data &&
496
+ entry.data.tool_name === 'bob.code.search' &&
497
+ entry.data.spec_id === specId &&
498
+ entry.data.event_type === 'tool_call_completed') {
499
+ searchCount++;
500
+ }
501
+ }
502
+ return searchCount;
503
+ }
504
+ export function validateDebuggerSearchUsage(specId, operation) {
505
+ // Only enforce for debug operations (commits and debug_log updates)
506
+ const debugOperations = ['debug_log', 'commit', 'fix_implementation'];
507
+ if (!debugOperations.some(op => operation.toLowerCase().includes(op))) {
508
+ return { valid: true };
509
+ }
510
+ const searchCount = countSearchToolUsage(specId);
511
+ if (searchCount < 1) {
512
+ return {
513
+ valid: false,
514
+ error: `Debugger must call bob.code.search at least once before ${operation}. Current search calls: ${searchCount}. Please perform search to understand the codebase before proceeding.`
515
+ };
516
+ }
517
+ return { valid: true };
518
+ }
519
+ // 3-Gate Approval System Implementation
520
+ export async function processApprovalGate(specId, gateNumber, context) {
521
+ try {
522
+ let approved = false;
523
+ let nextStep = "";
524
+ let message = "";
525
+ let blockingReason;
526
+ switch (gateNumber) {
527
+ case 1: // Q&A Clarification Gate
528
+ approved = validateGate1(context.confidence || 0, context.questions || []);
529
+ if (approved) {
530
+ nextStep = "spec_review";
531
+ message = "Confidence threshold met (≥95%). Proceeding to architecture planning.";
532
+ // Validate state transition
533
+ await validateEnhancedStateTransition({ from: "questioning", to: "spec_review", spec_id: specId }, { confidence_threshold: true });
534
+ }
535
+ else {
536
+ nextStep = "questioning";
537
+ blockingReason = `Confidence too low (${Math.round((context.confidence || 0) * 100)}%) or questions remain`;
538
+ message = `Gate 1 blocked: ${blockingReason}. Please provide clarifications.`;
539
+ }
540
+ break;
541
+ case 2: // Spec Approval Gate
542
+ approved = validateGate2(context.userApproval || false, context.sectionsComplete || []);
543
+ if (approved) {
544
+ nextStep = "approved";
545
+ message = "Specification approved. Ready for implementation.";
546
+ // Validate state transition
547
+ await validateEnhancedStateTransition({ from: "spec_review", to: "approved", spec_id: specId }, { confidence_threshold: true, user_approval: true });
548
+ }
549
+ else {
550
+ nextStep = "spec_review";
551
+ blockingReason = "Specification not approved or missing required sections";
552
+ message = `Gate 2 blocked: ${blockingReason}. Please complete spec and get approval.`;
553
+ }
554
+ break;
555
+ case 3: // Implementation Approval Gate (with mandatory Reviewer)
556
+ approved = validateGate3(context.testsPass || false, context.reviewerComplete || false, context.userApproval || false);
557
+ if (approved) {
558
+ nextStep = "completed";
559
+ message = "Implementation approved after testing and review. Ready for deployment.";
560
+ // Validate state transition
561
+ await validateEnhancedStateTransition({ from: "user_review", to: "completed", spec_id: specId }, {
562
+ confidence_threshold: true,
563
+ reviewer_completed: true,
564
+ user_approval: true
565
+ });
566
+ }
567
+ else {
568
+ nextStep = "user_review";
569
+ const reasons = [];
570
+ if (!context.testsPass)
571
+ reasons.push("tests not passing");
572
+ if (!context.reviewerComplete)
573
+ reasons.push("reviewer not completed");
574
+ if (!context.userApproval)
575
+ reasons.push("user approval pending");
576
+ blockingReason = reasons.join(", ");
577
+ message = `Gate 3 blocked: ${blockingReason}. Please address issues before proceeding.`;
578
+ }
579
+ break;
580
+ }
581
+ // Log gate processing
582
+ await mcpLogger.info(`Gate ${gateNumber} processed for ${specId}`, {
583
+ approved,
584
+ nextStep,
585
+ blockingReason,
586
+ gateNumber,
587
+ spec_id: specId
588
+ });
589
+ return { approved, nextStep, message, blockingReason };
590
+ }
591
+ catch (error) {
592
+ const errorMessage = `Gate ${gateNumber} validation failed: ${error instanceof Error ? error.message : String(error)}`;
593
+ await mcpLogger.error(errorMessage, { specId, gateNumber, error });
594
+ return {
595
+ approved: false,
596
+ nextStep: "error",
597
+ message: errorMessage,
598
+ blockingReason: "Validation error"
599
+ };
600
+ }
601
+ }
602
+ // Enhanced workflow router with 3-gate integration
603
+ export async function routeWithApprovalGates(specId, currentState, context) {
604
+ // Determine which gate to process based on current state
605
+ let gateNumber = null;
606
+ if (currentState === "questioning" && context.confidence >= 0.95) {
607
+ gateNumber = 1;
608
+ }
609
+ else if (currentState === "spec_review" && context.requestingApproval) {
610
+ gateNumber = 2;
611
+ }
612
+ else if (currentState === "implementation" && context.implementationComplete) {
613
+ gateNumber = 3;
614
+ }
615
+ if (gateNumber) {
616
+ const gateResult = await processApprovalGate(specId, gateNumber, context);
617
+ if (gateResult.approved) {
618
+ return {
619
+ action: "proceed",
620
+ nextMode: getNextMode(gateResult.nextStep),
621
+ gateResult
622
+ };
623
+ }
624
+ else {
625
+ return {
626
+ action: "await_approval",
627
+ gateResult
628
+ };
629
+ }
630
+ }
631
+ // No gate processing needed, continue with normal routing
632
+ return {
633
+ action: "route",
634
+ nextMode: context.suggestedMode
635
+ };
636
+ }
637
+ function getNextMode(nextStep) {
638
+ const modeMap = {
639
+ "spec_review": "architect",
640
+ "approved": "engineer",
641
+ "user_review": "reviewer",
642
+ "completed": "none"
643
+ };
644
+ return modeMap[nextStep] || "architect";
645
+ }
646
+ // Mandatory Reviewer Integration (TASK-006)
647
+ export async function enforceReviewerBeforeGate3(specId, context) {
648
+ // Check if implementation is complete and ready for Gate 3
649
+ if (!context.implementationComplete) {
650
+ return {
651
+ requiresReviewer: false,
652
+ reviewerComplete: false,
653
+ nextAction: "continue_implementation"
654
+ };
655
+ }
656
+ // Check if tests are passing (required before reviewer)
657
+ if (!context.testsPass) {
658
+ return {
659
+ requiresReviewer: false,
660
+ reviewerComplete: false,
661
+ nextAction: "fix_tests_first"
662
+ };
663
+ }
664
+ // Check if reviewer has already completed their work
665
+ const reviewerComplete = await checkReviewerCompletion(specId);
666
+ if (!reviewerComplete) {
667
+ // Auto-route to Reviewer with context
668
+ await mcpLogger.info(`Auto-routing to Reviewer before Gate 3 for ${specId}`, {
669
+ spec_id: specId,
670
+ reason: "mandatory_review_before_gate3",
671
+ tests_passing: context.testsPass
672
+ });
673
+ // Send dashboard update
674
+ await sendDashboardUpdate(specId, "auto_routing_reviewer", {
675
+ reason: "Gate 3 requires review",
676
+ tests_status: "passing"
677
+ });
678
+ return {
679
+ requiresReviewer: true,
680
+ reviewerComplete: false,
681
+ nextAction: "route_to_reviewer"
682
+ };
683
+ }
684
+ // Reviewer is complete, ready for Gate 3
685
+ return {
686
+ requiresReviewer: false,
687
+ reviewerComplete: true,
688
+ nextAction: "proceed_to_gate3"
689
+ };
690
+ }
691
+ async function checkReviewerCompletion(specId) {
692
+ try {
693
+ // Check for reviewer completion in the spec logs
694
+ const spec = await specGetHandler({ spec_id: specId });
695
+ // Look for reviewer completion in execution logs
696
+ const reviewerLogs = spec.execution_logs?.filter((log) => log.engineer?.includes("REVIEWER") || log.action?.includes("review")) || [];
697
+ // Also check debug logs for review findings
698
+ const reviewFindings = spec.debug_logs?.filter((log) => log.issue?.includes("review") || log.root_cause?.includes("review")) || [];
699
+ return reviewerLogs.length > 0 || reviewFindings.length > 0;
700
+ }
701
+ catch (error) {
702
+ await mcpLogger.warning(`Failed to check reviewer completion for ${specId}`, { error });
703
+ // Default to requiring review if we can't determine completion
704
+ return false;
705
+ }
706
+ }
707
+ // Enhanced implementation complete handler with reviewer enforcement
708
+ export async function handleImplementationComplete(specId, context) {
709
+ // Step 1: Check if tests are passing
710
+ if (!context.testsPass) {
711
+ return {
712
+ action: "fix_tests",
713
+ message: "Tests must pass before proceeding to review and approval. Please fix failing tests.",
714
+ };
715
+ }
716
+ // Step 2: Start dev server (moved from workflow.start)
717
+ if (!context.devServerStarted) {
718
+ await mcpLogger.info(`Starting dev server after tests pass for ${specId}`, { spec_id: specId });
719
+ // This would integrate with the server startup logic that was removed from workflow.start
720
+ await sendDashboardUpdate(specId, "starting_preview_server", {
721
+ reason: "tests_passed",
722
+ next_step: "review_and_approval"
723
+ });
724
+ return {
725
+ action: "start_server",
726
+ message: "Tests passed! Starting preview server before review and approval.",
727
+ };
728
+ }
729
+ // Step 3: Enforce mandatory reviewer
730
+ const reviewerCheck = await enforceReviewerBeforeGate3(specId, context);
731
+ if (reviewerCheck.requiresReviewer) {
732
+ return {
733
+ action: "route",
734
+ nextMode: "reviewer",
735
+ message: "Implementation complete and tests passing. Routing to Reviewer for mandatory review before final approval.",
736
+ };
737
+ }
738
+ if (reviewerCheck.nextAction === "fix_tests_first") {
739
+ return {
740
+ action: "fix_tests",
741
+ message: "Please ensure all tests are passing before proceeding to review.",
742
+ };
743
+ }
744
+ // Step 4: Process Gate 3 (all requirements met)
745
+ const gateResult = await processApprovalGate(specId, 3, {
746
+ ...context,
747
+ reviewerComplete: reviewerCheck.reviewerComplete
748
+ });
749
+ if (gateResult.approved) {
750
+ return {
751
+ action: "complete",
752
+ message: "Implementation approved! Ready for deployment.",
753
+ gateResult
754
+ };
755
+ }
756
+ else {
757
+ return {
758
+ action: "await_approval",
759
+ message: gateResult.message,
760
+ requiresApproval: true,
761
+ gateResult
762
+ };
763
+ }
764
+ }
765
+ //# sourceMappingURL=orchestratorTools.js.map