@hailer/mcp 0.1.15 → 0.1.17
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/.claude/agents/agent-giuseppe-app-builder.md +7 -6
- package/.claude/agents/agent-lars-code-inspector.md +26 -14
- package/dist/agents/bot-manager.d.ts +48 -0
- package/dist/agents/bot-manager.js +254 -0
- package/dist/agents/factory.d.ts +150 -0
- package/dist/agents/factory.js +650 -0
- package/dist/agents/giuseppe/ai.d.ts +83 -0
- package/dist/agents/giuseppe/ai.js +466 -0
- package/dist/agents/giuseppe/bot.d.ts +110 -0
- package/dist/agents/giuseppe/bot.js +780 -0
- package/dist/agents/giuseppe/config.d.ts +25 -0
- package/dist/agents/giuseppe/config.js +227 -0
- package/dist/agents/giuseppe/files.d.ts +52 -0
- package/dist/agents/giuseppe/files.js +338 -0
- package/dist/agents/giuseppe/git.d.ts +48 -0
- package/dist/agents/giuseppe/git.js +298 -0
- package/dist/agents/giuseppe/index.d.ts +97 -0
- package/dist/agents/giuseppe/index.js +258 -0
- package/dist/agents/giuseppe/lsp.d.ts +113 -0
- package/dist/agents/giuseppe/lsp.js +485 -0
- package/dist/agents/giuseppe/monitor.d.ts +118 -0
- package/dist/agents/giuseppe/monitor.js +621 -0
- package/dist/agents/giuseppe/prompt.d.ts +5 -0
- package/dist/agents/giuseppe/prompt.js +94 -0
- package/dist/agents/giuseppe/registries/pending-classification.d.ts +28 -0
- package/dist/agents/giuseppe/registries/pending-classification.js +50 -0
- package/dist/agents/giuseppe/registries/pending-fix.d.ts +30 -0
- package/dist/agents/giuseppe/registries/pending-fix.js +42 -0
- package/dist/agents/giuseppe/registries/pending.d.ts +27 -0
- package/dist/agents/giuseppe/registries/pending.js +49 -0
- package/dist/agents/giuseppe/specialist.d.ts +47 -0
- package/dist/agents/giuseppe/specialist.js +237 -0
- package/dist/agents/giuseppe/types.d.ts +123 -0
- package/dist/agents/giuseppe/types.js +9 -0
- package/dist/agents/hailer-expert/index.d.ts +8 -0
- package/dist/agents/hailer-expert/index.js +14 -0
- package/dist/agents/hal/daemon.d.ts +142 -0
- package/dist/agents/hal/daemon.js +1103 -0
- package/dist/agents/hal/definitions.d.ts +55 -0
- package/dist/agents/hal/definitions.js +263 -0
- package/dist/agents/hal/index.d.ts +3 -0
- package/dist/agents/hal/index.js +8 -0
- package/dist/agents/index.d.ts +18 -0
- package/dist/agents/index.js +48 -0
- package/dist/agents/shared/base.d.ts +216 -0
- package/dist/agents/shared/base.js +846 -0
- package/dist/agents/shared/services/agent-registry.d.ts +107 -0
- package/dist/agents/shared/services/agent-registry.js +629 -0
- package/dist/agents/shared/services/conversation-manager.d.ts +50 -0
- package/dist/agents/shared/services/conversation-manager.js +136 -0
- package/dist/agents/shared/services/mcp-client.d.ts +56 -0
- package/dist/agents/shared/services/mcp-client.js +124 -0
- package/dist/agents/shared/services/message-classifier.d.ts +37 -0
- package/dist/agents/shared/services/message-classifier.js +187 -0
- package/dist/agents/shared/services/message-formatter.d.ts +89 -0
- package/dist/agents/shared/services/message-formatter.js +371 -0
- package/dist/agents/shared/services/session-logger.d.ts +106 -0
- package/dist/agents/shared/services/session-logger.js +446 -0
- package/dist/agents/shared/services/tool-executor.d.ts +41 -0
- package/dist/agents/shared/services/tool-executor.js +169 -0
- package/dist/agents/shared/services/workspace-schema-cache.d.ts +125 -0
- package/dist/agents/shared/services/workspace-schema-cache.js +578 -0
- package/dist/agents/shared/specialist.d.ts +91 -0
- package/dist/agents/shared/specialist.js +399 -0
- package/dist/agents/shared/tool-schema-loader.d.ts +62 -0
- package/dist/agents/shared/tool-schema-loader.js +232 -0
- package/dist/agents/shared/types.d.ts +327 -0
- package/dist/agents/shared/types.js +121 -0
- package/dist/app.js +21 -4
- package/dist/cli.js +0 -0
- package/dist/client/agents/orchestrator.d.ts +1 -0
- package/dist/client/agents/orchestrator.js +12 -1
- package/dist/commands/seed-config.d.ts +9 -0
- package/dist/commands/seed-config.js +372 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +61 -1
- package/dist/core.d.ts +8 -0
- package/dist/core.js +137 -6
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/mcp/UserContextCache.js +2 -2
- package/dist/mcp/hailer-clients.d.ts +15 -0
- package/dist/mcp/hailer-clients.js +100 -6
- package/dist/mcp/signal-handler.d.ts +16 -5
- package/dist/mcp/signal-handler.js +173 -122
- package/dist/mcp/tools/activity.js +9 -1
- package/dist/mcp/tools/bot-config.d.ts +184 -9
- package/dist/mcp/tools/bot-config.js +2177 -163
- package/dist/mcp/tools/giuseppe-tools.d.ts +21 -0
- package/dist/mcp/tools/giuseppe-tools.js +525 -0
- package/dist/mcp/utils/hailer-api-client.d.ts +42 -1
- package/dist/mcp/utils/hailer-api-client.js +128 -2
- package/dist/mcp/webhook-handler.d.ts +87 -0
- package/dist/mcp/webhook-handler.js +345 -0
- package/dist/mcp/workspace-cache.d.ts +5 -0
- package/dist/mcp/workspace-cache.js +11 -0
- package/dist/mcp-server.js +60 -5
- package/dist/modules/bug-reports/giuseppe-agent.d.ts +58 -0
- package/dist/modules/bug-reports/giuseppe-agent.js +467 -0
- package/dist/modules/bug-reports/giuseppe-ai.d.ts +25 -1
- package/dist/modules/bug-reports/giuseppe-ai.js +133 -2
- package/dist/modules/bug-reports/giuseppe-bot.d.ts +2 -2
- package/dist/modules/bug-reports/giuseppe-bot.js +66 -42
- package/dist/modules/bug-reports/giuseppe-daemon.d.ts +80 -0
- package/dist/modules/bug-reports/giuseppe-daemon.js +617 -0
- package/dist/modules/bug-reports/giuseppe-files.d.ts +12 -0
- package/dist/modules/bug-reports/giuseppe-files.js +37 -0
- package/dist/modules/bug-reports/giuseppe-lsp.d.ts +84 -13
- package/dist/modules/bug-reports/giuseppe-lsp.js +403 -61
- package/dist/modules/bug-reports/index.d.ts +1 -0
- package/dist/modules/bug-reports/index.js +31 -29
- 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 -
|
|
42
|
+
* Handle a message in a bug discussion - uses LLM to understand intent
|
|
43
43
|
*/
|
|
44
|
-
handleDiscussionMessage(discussionId: string, message: string,
|
|
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
|
-
//
|
|
243
|
-
|
|
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
|
-
|
|
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 -
|
|
397
|
+
* Handle a message in a bug discussion - uses LLM to understand intent
|
|
385
398
|
*/
|
|
386
|
-
async handleDiscussionMessage(discussionId, message,
|
|
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
|
|
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
|
-
//
|
|
400
|
-
const
|
|
401
|
-
logger.debug('
|
|
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
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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 (
|
|
413
|
-
logger.debug('
|
|
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 (
|
|
417
|
-
logger.debug('
|
|
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
|
-
|
|
421
|
-
|
|
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
|
|
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
|
-
//
|
|
430
|
-
|
|
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
|
-
|
|
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:
|
|
448
|
-
published:
|
|
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
|