@hailer/mcp 0.1.15 → 0.1.16

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 (112) hide show
  1. package/.claude/agents/agent-giuseppe-app-builder.md +7 -6
  2. package/.claude/agents/agent-lars-code-inspector.md +26 -14
  3. package/dist/agents/bot-manager.d.ts +48 -0
  4. package/dist/agents/bot-manager.js +254 -0
  5. package/dist/agents/factory.d.ts +150 -0
  6. package/dist/agents/factory.js +650 -0
  7. package/dist/agents/giuseppe/ai.d.ts +83 -0
  8. package/dist/agents/giuseppe/ai.js +466 -0
  9. package/dist/agents/giuseppe/bot.d.ts +110 -0
  10. package/dist/agents/giuseppe/bot.js +780 -0
  11. package/dist/agents/giuseppe/config.d.ts +25 -0
  12. package/dist/agents/giuseppe/config.js +227 -0
  13. package/dist/agents/giuseppe/files.d.ts +52 -0
  14. package/dist/agents/giuseppe/files.js +338 -0
  15. package/dist/agents/giuseppe/git.d.ts +48 -0
  16. package/dist/agents/giuseppe/git.js +298 -0
  17. package/dist/agents/giuseppe/index.d.ts +97 -0
  18. package/dist/agents/giuseppe/index.js +258 -0
  19. package/dist/agents/giuseppe/lsp.d.ts +113 -0
  20. package/dist/agents/giuseppe/lsp.js +485 -0
  21. package/dist/agents/giuseppe/monitor.d.ts +118 -0
  22. package/dist/agents/giuseppe/monitor.js +621 -0
  23. package/dist/agents/giuseppe/prompt.d.ts +5 -0
  24. package/dist/agents/giuseppe/prompt.js +94 -0
  25. package/dist/agents/giuseppe/registries/pending-classification.d.ts +28 -0
  26. package/dist/agents/giuseppe/registries/pending-classification.js +50 -0
  27. package/dist/agents/giuseppe/registries/pending-fix.d.ts +30 -0
  28. package/dist/agents/giuseppe/registries/pending-fix.js +42 -0
  29. package/dist/agents/giuseppe/registries/pending.d.ts +27 -0
  30. package/dist/agents/giuseppe/registries/pending.js +49 -0
  31. package/dist/agents/giuseppe/specialist.d.ts +47 -0
  32. package/dist/agents/giuseppe/specialist.js +237 -0
  33. package/dist/agents/giuseppe/types.d.ts +123 -0
  34. package/dist/agents/giuseppe/types.js +9 -0
  35. package/dist/agents/hailer-expert/index.d.ts +8 -0
  36. package/dist/agents/hailer-expert/index.js +14 -0
  37. package/dist/agents/hal/daemon.d.ts +142 -0
  38. package/dist/agents/hal/daemon.js +1103 -0
  39. package/dist/agents/hal/definitions.d.ts +55 -0
  40. package/dist/agents/hal/definitions.js +263 -0
  41. package/dist/agents/hal/index.d.ts +3 -0
  42. package/dist/agents/hal/index.js +8 -0
  43. package/dist/agents/index.d.ts +18 -0
  44. package/dist/agents/index.js +48 -0
  45. package/dist/agents/shared/base.d.ts +216 -0
  46. package/dist/agents/shared/base.js +846 -0
  47. package/dist/agents/shared/services/agent-registry.d.ts +107 -0
  48. package/dist/agents/shared/services/agent-registry.js +629 -0
  49. package/dist/agents/shared/services/conversation-manager.d.ts +50 -0
  50. package/dist/agents/shared/services/conversation-manager.js +136 -0
  51. package/dist/agents/shared/services/mcp-client.d.ts +56 -0
  52. package/dist/agents/shared/services/mcp-client.js +124 -0
  53. package/dist/agents/shared/services/message-classifier.d.ts +37 -0
  54. package/dist/agents/shared/services/message-classifier.js +187 -0
  55. package/dist/agents/shared/services/message-formatter.d.ts +89 -0
  56. package/dist/agents/shared/services/message-formatter.js +371 -0
  57. package/dist/agents/shared/services/session-logger.d.ts +106 -0
  58. package/dist/agents/shared/services/session-logger.js +446 -0
  59. package/dist/agents/shared/services/tool-executor.d.ts +41 -0
  60. package/dist/agents/shared/services/tool-executor.js +169 -0
  61. package/dist/agents/shared/services/workspace-schema-cache.d.ts +125 -0
  62. package/dist/agents/shared/services/workspace-schema-cache.js +578 -0
  63. package/dist/agents/shared/specialist.d.ts +91 -0
  64. package/dist/agents/shared/specialist.js +399 -0
  65. package/dist/agents/shared/tool-schema-loader.d.ts +62 -0
  66. package/dist/agents/shared/tool-schema-loader.js +232 -0
  67. package/dist/agents/shared/types.d.ts +327 -0
  68. package/dist/agents/shared/types.js +121 -0
  69. package/dist/app.js +21 -4
  70. package/dist/cli.js +0 -0
  71. package/dist/client/agents/orchestrator.d.ts +1 -0
  72. package/dist/client/agents/orchestrator.js +12 -1
  73. package/dist/commands/seed-config.d.ts +9 -0
  74. package/dist/commands/seed-config.js +372 -0
  75. package/dist/config.d.ts +10 -0
  76. package/dist/config.js +61 -1
  77. package/dist/core.d.ts +8 -0
  78. package/dist/core.js +137 -6
  79. package/dist/lib/discussion-lock.d.ts +42 -0
  80. package/dist/lib/discussion-lock.js +110 -0
  81. package/dist/mcp/UserContextCache.js +2 -2
  82. package/dist/mcp/hailer-clients.d.ts +15 -0
  83. package/dist/mcp/hailer-clients.js +100 -6
  84. package/dist/mcp/signal-handler.d.ts +16 -5
  85. package/dist/mcp/signal-handler.js +173 -122
  86. package/dist/mcp/tools/activity.js +9 -1
  87. package/dist/mcp/tools/bot-config.d.ts +184 -9
  88. package/dist/mcp/tools/bot-config.js +2177 -163
  89. package/dist/mcp/tools/giuseppe-tools.d.ts +21 -0
  90. package/dist/mcp/tools/giuseppe-tools.js +525 -0
  91. package/dist/mcp/utils/hailer-api-client.d.ts +42 -1
  92. package/dist/mcp/utils/hailer-api-client.js +128 -2
  93. package/dist/mcp/webhook-handler.d.ts +87 -0
  94. package/dist/mcp/webhook-handler.js +343 -0
  95. package/dist/mcp/workspace-cache.d.ts +5 -0
  96. package/dist/mcp/workspace-cache.js +11 -0
  97. package/dist/mcp-server.js +55 -5
  98. package/dist/modules/bug-reports/giuseppe-agent.d.ts +58 -0
  99. package/dist/modules/bug-reports/giuseppe-agent.js +467 -0
  100. package/dist/modules/bug-reports/giuseppe-ai.d.ts +25 -1
  101. package/dist/modules/bug-reports/giuseppe-ai.js +133 -2
  102. package/dist/modules/bug-reports/giuseppe-bot.d.ts +2 -2
  103. package/dist/modules/bug-reports/giuseppe-bot.js +66 -42
  104. package/dist/modules/bug-reports/giuseppe-daemon.d.ts +80 -0
  105. package/dist/modules/bug-reports/giuseppe-daemon.js +617 -0
  106. package/dist/modules/bug-reports/giuseppe-files.d.ts +12 -0
  107. package/dist/modules/bug-reports/giuseppe-files.js +37 -0
  108. package/dist/modules/bug-reports/giuseppe-lsp.d.ts +84 -13
  109. package/dist/modules/bug-reports/giuseppe-lsp.js +403 -61
  110. package/dist/modules/bug-reports/index.d.ts +1 -0
  111. package/dist/modules/bug-reports/index.js +31 -29
  112. package/package.json +3 -2
@@ -68,12 +68,107 @@ Respond with JSON only:
68
68
  return { classification: 'unclear', reason: 'Classification error' };
69
69
  }
70
70
  }
71
+ /**
72
+ * Detect user intent from a message (replaces magic word detection)
73
+ * Returns the intent and confidence level
74
+ */
75
+ async detectIntent(message, context) {
76
+ if (!this.anthropic) {
77
+ // Fallback to simple keyword matching
78
+ const lower = message.toLowerCase().trim();
79
+ if (context === 'classification') {
80
+ if (lower.includes('fix') && !lower.includes('don\'t') && !lower.includes('not')) {
81
+ return { intent: 'fix_it', confidence: 'medium', explanation: 'Contains "fix"' };
82
+ }
83
+ if (lower.includes('not a bug') || lower.includes('feature')) {
84
+ return { intent: 'not_a_bug', confidence: 'medium', explanation: 'Contains "not a bug" or "feature"' };
85
+ }
86
+ }
87
+ else {
88
+ if (lower === 'approved' || lower === 'yes' || lower === 'ship it' || lower === 'deploy') {
89
+ return { intent: 'approved', confidence: 'medium', explanation: 'Approval keyword' };
90
+ }
91
+ if (lower === 'denied' || lower === 'no' || lower === 'revert' || lower === 'cancel') {
92
+ return { intent: 'denied', confidence: 'medium', explanation: 'Denial keyword' };
93
+ }
94
+ }
95
+ return { intent: 'unknown', confidence: 'low', explanation: 'No matching keywords' };
96
+ }
97
+ try {
98
+ const contextDescription = context === 'classification'
99
+ ? `Giuseppe classified a bug report and is waiting for the user to confirm:
100
+ - "fix_it" = User wants Giuseppe to proceed with fixing the bug
101
+ - "not_a_bug" = User says this isn't a bug, it's a feature request or not an issue
102
+ - "clarify" = User is asking a question or providing more info
103
+ - "unknown" = Can't determine intent`
104
+ : `Giuseppe has created a fix and is waiting for approval to publish:
105
+ - "approved" = User approves publishing to production
106
+ - "denied" = User rejects the fix or wants changes
107
+ - "retry" = User wants Giuseppe to try a different approach
108
+ - "question" = User is asking about the fix
109
+ - "unknown" = Can't determine intent`;
110
+ const prompt = `Detect the user's intent from this message.
111
+
112
+ Context: ${contextDescription}
113
+
114
+ User message: "${message}"
115
+
116
+ Respond with JSON only (no markdown):
117
+ {"intent": "...", "confidence": "high|medium|low", "explanation": "brief reason"}`;
118
+ const response = await this.anthropic.messages.create({
119
+ model: 'claude-haiku-4-5-20251001',
120
+ max_tokens: 150,
121
+ messages: [{ role: 'user', content: prompt }]
122
+ });
123
+ const content = response.content[0];
124
+ if (content.type !== 'text') {
125
+ return { intent: 'unknown', confidence: 'low', explanation: 'AI response error' };
126
+ }
127
+ const parsed = JSON.parse(content.text.replace(/```json\n?|\n?```/g, '').trim());
128
+ logger.info('Intent detected', {
129
+ message: message.substring(0, 50),
130
+ intent: parsed.intent,
131
+ confidence: parsed.confidence
132
+ });
133
+ return {
134
+ intent: parsed.intent || 'unknown',
135
+ confidence: parsed.confidence || 'low',
136
+ explanation: parsed.explanation || ''
137
+ };
138
+ }
139
+ catch (error) {
140
+ logger.error('Intent detection failed', { error, message: message.substring(0, 50) });
141
+ return { intent: 'unknown', confidence: 'low', explanation: 'Detection error' };
142
+ }
143
+ }
144
+ /**
145
+ * Extract keywords from bug description for LSP filtering
146
+ */
147
+ extractKeywords(description) {
148
+ // Common technical terms to look for
149
+ const techTerms = [
150
+ 'click', 'drag', 'drop', 'scroll', 'hover', 'focus', 'blur',
151
+ 'render', 'update', 'load', 'save', 'delete', 'create',
152
+ 'button', 'input', 'form', 'modal', 'menu', 'list', 'table',
153
+ 'position', 'animation', 'transition', 'style', 'color',
154
+ 'error', 'crash', 'freeze', 'slow', 'lag', 'broken'
155
+ ];
156
+ const words = description.toLowerCase()
157
+ .replace(/[^\w\s]/g, ' ')
158
+ .split(/\s+/)
159
+ .filter(w => w.length > 2);
160
+ // Find matching tech terms
161
+ const matchedTerms = techTerms.filter(term => words.some(w => w.includes(term) || term.includes(w)));
162
+ // Also include unique words from description (nouns/verbs likely)
163
+ const uniqueWords = words.filter(w => w.length > 4 && !['the', 'and', 'but', 'for', 'not', 'with', 'this', 'that', 'when', 'have', 'from'].includes(w));
164
+ return [...new Set([...matchedTerms, ...uniqueWords])].slice(0, 10);
165
+ }
71
166
  /**
72
167
  * Analyze bug and generate fix plan using Claude (two-phase approach)
73
168
  * Phase 1: Send file list -> Claude picks relevant files
74
169
  * Phase 2: Send those files -> Claude generates fix
75
170
  */
76
- async analyzeAndPlanFix(bug, app, allFiles, readFiles) {
171
+ async analyzeAndPlanFix(bug, app, allFiles, readFiles, runLspAnalysis) {
77
172
  if (!this.anthropic) {
78
173
  logger.warn('Anthropic client not initialized - cannot generate fix');
79
174
  return null;
@@ -120,9 +215,45 @@ Return ONLY a JSON array of file paths, maximum 10 files. Example: ["src/App.tsx
120
215
  relevantFiles = allFiles.filter(f => f.startsWith('src/')).slice(0, 5);
121
216
  }
122
217
  logger.info('Phase 1 complete: Files to examine', { files: relevantFiles });
218
+ // Phase 1.5: Run TARGETED LSP analysis on relevant files only
219
+ // Extract keywords from bug description for filtering
220
+ const bugKeywords = this.extractKeywords(bug.description);
221
+ logger.info('Extracted bug keywords for LSP filtering', { keywords: bugKeywords });
222
+ let lspFindings = null;
223
+ if (runLspAnalysis) {
224
+ lspFindings = await runLspAnalysis(relevantFiles, bugKeywords);
225
+ logger.info('Targeted LSP analysis complete', {
226
+ files: relevantFiles.length,
227
+ unusedProps: lspFindings.unusedProps.length,
228
+ issues: lspFindings.issues.length
229
+ });
230
+ }
123
231
  // Phase 2: Read selected files and generate fix
124
232
  const sourceFiles = await readFiles(relevantFiles);
125
233
  logger.info('Phase 2: Generating fix', { filesRead: sourceFiles.length });
234
+ // Build LSP findings section if available
235
+ let lspSection = '';
236
+ if (lspFindings && (lspFindings.unusedProps.length > 0 || lspFindings.issues.length > 0)) {
237
+ lspSection = '\n\n=== LSP CODE ANALYSIS (HIGH PRIORITY) ===\n';
238
+ lspSection += 'IMPORTANT: These are REAL code issues detected by static analysis. Fix these FIRST.\n\n';
239
+ if (lspFindings.unusedProps.length > 0) {
240
+ lspSection += 'UNUSED PROPS (declared in interface but never used in component):\n';
241
+ lspFindings.unusedProps.forEach(p => {
242
+ lspSection += `- ${p.file}:${p.line} - prop "${p.name}" is declared but never destructured/used\n`;
243
+ // Add hint if prop name matches bug keywords
244
+ if (bug.description.toLowerCase().includes(p.name.toLowerCase())) {
245
+ lspSection += ` ^ THIS PROP NAME MATCHES THE BUG DESCRIPTION - VERY LIKELY THE ROOT CAUSE!\n`;
246
+ }
247
+ });
248
+ }
249
+ if (lspFindings.issues.length > 0) {
250
+ lspSection += '\nDEAD CODE (functions/variables declared but never called):\n';
251
+ lspFindings.issues.forEach(i => {
252
+ lspSection += `- ${i.file}:${i.line} - ${i.message}\n`;
253
+ });
254
+ }
255
+ lspSection += '\nThe fix should USE these unused props/functions, not add workaround code.\n';
256
+ }
126
257
  const phase2Prompt = `
127
258
  Bug Report:
128
259
  - Title: ${bug.name}
@@ -131,7 +262,7 @@ ${bug.stepsToReproduce ? `- Steps to reproduce: ${bug.stepsToReproduce}` : ''}
131
262
  ${bug.expectedBehavior ? `- Expected: ${bug.expectedBehavior}` : ''}
132
263
  ${bug.actualBehavior ? `- Actual: ${bug.actualBehavior}` : ''}
133
264
  - Priority: ${bug.priority || 'unknown'}
134
-
265
+ ${lspSection}
135
266
  App: ${app.name}
136
267
  Project path: ${app.projectPath}
137
268
 
@@ -39,9 +39,9 @@ export declare class GiuseppeBot {
39
39
  */
40
40
  proceedWithFix(bug: BugReport): Promise<FixResult>;
41
41
  /**
42
- * Handle a message in a bug discussion - state machine for approval flow
42
+ * Handle a message in a bug discussion - uses LLM to understand intent
43
43
  */
44
- handleDiscussionMessage(discussionId: string, message: string, senderId: string): Promise<{
44
+ handleDiscussionMessage(discussionId: string, message: string, _senderId: string): Promise<{
45
45
  approved: boolean;
46
46
  published: boolean;
47
47
  retrying?: boolean;
@@ -21,12 +21,8 @@ const giuseppe_ai_1 = require("./giuseppe-ai");
21
21
  const giuseppe_git_1 = require("./giuseppe-git");
22
22
  const giuseppe_files_1 = require("./giuseppe-files");
23
23
  const giuseppe_lsp_1 = require("./giuseppe-lsp");
24
+ const discussion_lock_1 = require("../../lib/discussion-lock");
24
25
  const logger = (0, logger_1.createLogger)({ component: 'giuseppe-bot' });
25
- // Magic words for approval flow
26
- const MAGIC_WORD_APPROVED = 'approved';
27
- const MAGIC_WORD_DENIED = 'denied';
28
- const MAGIC_WORD_FIX_IT = 'fix it';
29
- const MAGIC_WORD_NOT_A_BUG = 'not a bug';
30
26
  // Session Log (Context Log) field IDs for logging
31
27
  const SESSION_LOG_CONFIG = {
32
28
  workflowId: '695784898d347a6c707ee397',
@@ -133,6 +129,10 @@ class GiuseppeBot {
133
129
  bugName: bug.name,
134
130
  appId: bug.appId
135
131
  });
132
+ // Acquire lock on this discussion to prevent other bots (Orchestrator) from responding
133
+ if (bug.discussionId) {
134
+ (0, discussion_lock_1.acquireDiscussionLock)(bug.discussionId, 'giuseppe', 10 * 60 * 1000); // 10 minute lock
135
+ }
136
136
  const result = {
137
137
  success: false,
138
138
  summary: '',
@@ -239,15 +239,28 @@ class GiuseppeBot {
239
239
  }
240
240
  result.log?.push(`Found app project: ${app.projectPath}`);
241
241
  await this.reportProgress(bug, `📁 Found app project: ${app.name}`);
242
- // 1.5. Run LSP analysis to understand current codebase state
243
- const preAnalysis = await this.lsp.analyzeProject(app.projectPath);
244
- if (preAnalysis.issues.length > 0) {
245
- result.log?.push(`Pre-fix LSP: ${preAnalysis.summary}`);
246
- await this.reportProgress(bug, `🔎 Codebase analysis: ${preAnalysis.summary}`);
247
- }
242
+ // Note: LSP analysis now runs AFTER Phase 1 identifies relevant files
243
+ // This is more efficient - we only analyze files related to the bug
248
244
  // 2. Analyze and generate fix
249
245
  const allFiles = await this.files.scanSourceFiles(app.projectPath);
250
- const fixPlan = await this.ai.analyzeAndPlanFix(bug, app, allFiles, (paths) => this.files.readSelectedFiles(app.projectPath, paths));
246
+ // Create LSP analysis callback - runs TARGETED analysis on specific files
247
+ const runLspAnalysis = async (files, keywords) => {
248
+ logger.info('Running targeted LSP analysis', { files: files.length, keywords });
249
+ const analysis = await this.lsp.analyzeRelevantFiles(app.projectPath, files, keywords);
250
+ return {
251
+ unusedProps: (analysis.unusedProps || []).map((p) => ({
252
+ name: p.name,
253
+ line: p.line,
254
+ file: p.file || 'unknown'
255
+ })),
256
+ issues: (analysis.issues || []).map(i => ({
257
+ file: i.file,
258
+ line: i.line,
259
+ message: i.message
260
+ }))
261
+ };
262
+ };
263
+ const fixPlan = await this.ai.analyzeAndPlanFix(bug, app, allFiles, (paths) => this.files.readSelectedFiles(app.projectPath, paths), runLspAnalysis);
251
264
  if (!fixPlan) {
252
265
  result.summary = 'Could not generate fix plan';
253
266
  result.log?.push('Fix plan generation failed');
@@ -381,56 +394,64 @@ ${currentFixPlan.explanation}
381
394
  }
382
395
  }
383
396
  /**
384
- * Handle a message in a bug discussion - state machine for approval flow
397
+ * Handle a message in a bug discussion - uses LLM to understand intent
385
398
  */
386
- async handleDiscussionMessage(discussionId, message, senderId) {
387
- // Normalize: replace non-breaking spaces (0xA0) with regular spaces (0x20)
388
- // Hailer chat may send NBSP instead of regular spaces
389
- const messageLower = message.toLowerCase().replace(/\u00A0/g, ' ').trim();
399
+ async handleDiscussionMessage(discussionId, message, _senderId) {
390
400
  logger.debug('handleDiscussionMessage called', {
391
401
  discussionId,
392
402
  message: message.substring(0, 50),
393
403
  pendingClassificationsSize: pending_classification_registry_1.pendingClassificationRegistry.size,
394
404
  hasPendingForDiscussion: pending_classification_registry_1.pendingClassificationRegistry.has(discussionId)
395
405
  });
396
- // Check for pending classification first (waiting for "fix it" or "not a bug")
406
+ // Check for pending classification first (waiting for user to confirm fix or mark as not-a-bug)
397
407
  const pendingClassification = pending_classification_registry_1.pendingClassificationRegistry.get(discussionId);
398
408
  if (pendingClassification) {
399
- // Hex dump for debugging character encoding issues
400
- const hexDump = (str) => Array.from(str).map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(' ');
401
- logger.debug('Checking message against magic words', {
409
+ // Use LLM to understand user intent (replaces hardcoded magic words)
410
+ const { intent, confidence } = await this.ai.detectIntent(message, 'classification');
411
+ logger.debug('Classification intent detected', {
402
412
  discussionId,
403
- messageLower,
404
- messageLowerHex: hexDump(messageLower),
405
- MAGIC_WORD_NOT_A_BUG,
406
- magicWordHex: hexDump(MAGIC_WORD_NOT_A_BUG),
407
- isFixIt: messageLower === MAGIC_WORD_FIX_IT || messageLower === 'fix',
408
- isNotABug: messageLower === MAGIC_WORD_NOT_A_BUG,
409
- includesNotABug: messageLower.includes('not a bug'),
410
- includesFeature: messageLower.includes('feature')
413
+ message: message.substring(0, 50),
414
+ intent,
415
+ confidence
411
416
  });
412
- if (messageLower === MAGIC_WORD_FIX_IT || messageLower === 'fix') {
413
- logger.debug('Matched fix it - calling handleFixItConfirmation');
417
+ if (intent === 'fix_it' && confidence !== 'low') {
418
+ logger.debug('User wants to fix - calling handleFixItConfirmation');
414
419
  return await this.handleFixItConfirmation(discussionId, pendingClassification);
415
420
  }
416
- if (messageLower === MAGIC_WORD_NOT_A_BUG || messageLower.includes('not a bug') || messageLower.includes('feature')) {
417
- logger.debug('Matched not a bug - calling handleNotABugConfirmation');
421
+ if (intent === 'not_a_bug' && confidence !== 'low') {
422
+ logger.debug('User says not a bug - calling handleNotABugConfirmation');
418
423
  return await this.handleNotABugConfirmation(discussionId, pendingClassification);
419
424
  }
420
- logger.debug('No magic word match - ignoring message');
421
- // Other messages while waiting for classification - ignore
425
+ // Unknown or clarify intent - ignore and wait for clearer response
426
+ logger.debug('Unclear intent - waiting for clearer response', { intent, confidence });
422
427
  return { approved: false, published: false };
423
428
  }
424
- // Check for pending fix (waiting for "approved" or "denied")
429
+ // Check for pending fix (waiting for approval to publish)
425
430
  const pendingFix = pending_fix_registry_1.pendingFixRegistry.get(discussionId);
426
431
  if (!pendingFix) {
427
432
  return { approved: false, published: false };
428
433
  }
429
- // Giuseppe ONLY handles "approved" - HAL handles everything else (denial, explanations)
430
- if (messageLower === MAGIC_WORD_APPROVED) {
434
+ // Use LLM to understand approval intent
435
+ const { intent, confidence } = await this.ai.detectIntent(message, 'approval');
436
+ logger.debug('Approval intent detected', {
437
+ discussionId,
438
+ message: message.substring(0, 50),
439
+ intent,
440
+ confidence
441
+ });
442
+ if (intent === 'approved' && confidence !== 'low') {
431
443
  return await this.handleApproval(discussionId, pendingFix);
432
444
  }
433
- // All other messages: return false, let HAL handle
445
+ if (intent === 'denied' && confidence !== 'low') {
446
+ // Handle denial - revert changes
447
+ return await this.handleDenial(discussionId, pendingFix);
448
+ }
449
+ if (intent === 'retry' && confidence !== 'low') {
450
+ // User wants to try again - not implemented yet
451
+ await this.sendDiscussionMessage(discussionId, "🔄 Retry is not implemented yet. Please say 'approved' to publish or 'denied' to cancel.");
452
+ return { approved: false, published: false };
453
+ }
454
+ // Unknown intent - let HAL or user clarify
434
455
  return { approved: false, published: false };
435
456
  }
436
457
  /**
@@ -441,11 +462,14 @@ ${currentFixPlan.explanation}
441
462
  // Remove from pending classifications
442
463
  pending_classification_registry_1.pendingClassificationRegistry.unregister(discussionId);
443
464
  await this.sendDiscussionMessage(discussionId, `✅ Got it! Starting bug fix...`);
444
- // Proceed with the actual fix
465
+ // Proceed with the actual fix (creates commit, stores pending fix awaiting approval)
445
466
  const result = await this.proceedWithFix(pending.bug);
467
+ // Return false for both - the CLASSIFICATION was approved ("fix it"),
468
+ // but the FIX itself is not yet approved (waiting for "approved" message)
469
+ // The fix was applied locally but NOT published to production
446
470
  return {
447
- approved: true,
448
- published: result.success,
471
+ approved: false, // Fix not approved yet, just classification
472
+ published: false, // Not published, just committed locally
449
473
  error: result.success ? undefined : result.summary
450
474
  };
451
475
  }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Giuseppe Daemon - LLM-Driven Bug Fixer Agent
3
+ *
4
+ * A fully autonomous AI agent for fixing bugs in Hailer apps.
5
+ * Unlike the old GiuseppeBot with hardcoded magic words, this daemon:
6
+ * - Understands natural language (no "fix it" / "approved" keywords)
7
+ * - Reasons about problems (git conflicts, build failures)
8
+ * - Decides when to ask for approval vs proceed
9
+ * - Has tools for all bug-fixing operations
10
+ */
11
+ import Anthropic from "@anthropic-ai/sdk";
12
+ import { ChatAgentDaemon, ChatAgentDaemonConfig, IncomingMessage } from "../../client/agents/base";
13
+ import { HailerSignal } from "../../mcp/signal-handler";
14
+ import { ToolInput } from "../../client/types";
15
+ import type { BugMonitor } from "./bug-monitor";
16
+ import type { UserContext } from "../../mcp/UserContextCache";
17
+ export interface GiuseppeDaemonConfig extends ChatAgentDaemonConfig {
18
+ /** Bug monitor for phase transitions */
19
+ bugMonitor: BugMonitor;
20
+ /** User context for API access */
21
+ userContext: UserContext;
22
+ /** Base path for app projects */
23
+ appsBasePath: string;
24
+ }
25
+ export declare class GiuseppeDaemon extends ChatAgentDaemon {
26
+ private giuseppeLogger;
27
+ private bugMonitor;
28
+ private userContext;
29
+ private appsBasePath;
30
+ private ai;
31
+ private git;
32
+ private files;
33
+ private lsp;
34
+ private bugDiscussions;
35
+ constructor(config: GiuseppeDaemonConfig);
36
+ /**
37
+ * Register a bug discussion so Giuseppe knows to respond to it
38
+ */
39
+ registerBugDiscussion(discussionId: string, bugId: string, bugName: string, workflowId: string): void;
40
+ /**
41
+ * Unregister a bug discussion (bug resolved/closed)
42
+ */
43
+ unregisterBugDiscussion(discussionId: string): void;
44
+ protected getAgentName(): {
45
+ firstName: string;
46
+ lastName: string;
47
+ };
48
+ protected getAgentDescription(): string;
49
+ protected getPositionDetails(): {
50
+ name: string;
51
+ purpose: string;
52
+ personaTone: string;
53
+ coreCapabilities: string;
54
+ boundaries: string;
55
+ };
56
+ /**
57
+ * Only respond to messages in registered bug discussions
58
+ */
59
+ protected extractIncomingMessage(signal: HailerSignal): Promise<IncomingMessage | null>;
60
+ /**
61
+ * Giuseppe's tools for bug fixing
62
+ */
63
+ protected getToolWhitelist(): string[];
64
+ /**
65
+ * Custom tools for bug fixing operations
66
+ */
67
+ protected getTools(): Anthropic.Tool[];
68
+ /**
69
+ * Execute Giuseppe's custom tools
70
+ */
71
+ protected executeCustomTool(name: string, rawInput: ToolInput): Promise<{
72
+ success: boolean;
73
+ result: any;
74
+ }>;
75
+ /**
76
+ * System prompt for Giuseppe - natural conversation about bug fixing
77
+ */
78
+ protected getSystemPrompt(): string;
79
+ }
80
+ //# sourceMappingURL=giuseppe-daemon.d.ts.map