@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.
- 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 +343 -0
- package/dist/mcp/workspace-cache.d.ts +5 -0
- package/dist/mcp/workspace-cache.js +11 -0
- package/dist/mcp-server.js +55 -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
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Giuseppe AI Module - Claude API interactions for bug analysis and fix generation
|
|
3
|
+
*/
|
|
4
|
+
import type { BugReport, AppRegistryEntry } from './types';
|
|
5
|
+
/** Fix plan from Claude analysis */
|
|
6
|
+
export interface FixPlan {
|
|
7
|
+
analysis: string;
|
|
8
|
+
debugTrace?: string;
|
|
9
|
+
failurePoint?: string;
|
|
10
|
+
rootCause: string;
|
|
11
|
+
fix: {
|
|
12
|
+
files: Array<{
|
|
13
|
+
path: string;
|
|
14
|
+
action: 'edit' | 'create' | 'delete';
|
|
15
|
+
search?: string;
|
|
16
|
+
replace?: string;
|
|
17
|
+
content?: string;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
explanation: string;
|
|
21
|
+
testSuggestions?: string[];
|
|
22
|
+
}
|
|
23
|
+
export type BugClassification = 'bug' | 'feature_request' | 'unclear';
|
|
24
|
+
export interface ClassificationResult {
|
|
25
|
+
classification: BugClassification;
|
|
26
|
+
reason: string;
|
|
27
|
+
}
|
|
28
|
+
export interface FileContent {
|
|
29
|
+
path: string;
|
|
30
|
+
content: string;
|
|
31
|
+
}
|
|
32
|
+
export declare class GiuseppeAI {
|
|
33
|
+
private anthropic?;
|
|
34
|
+
constructor(apiKey?: string);
|
|
35
|
+
get isAvailable(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Classify a report as bug or feature request
|
|
38
|
+
*/
|
|
39
|
+
classifyReport(bug: BugReport): Promise<ClassificationResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Detect user intent from a message (replaces magic word detection)
|
|
42
|
+
* Returns the intent and confidence level
|
|
43
|
+
*/
|
|
44
|
+
detectIntent(message: string, context: 'classification' | 'approval'): Promise<{
|
|
45
|
+
intent: string;
|
|
46
|
+
confidence: 'high' | 'medium' | 'low';
|
|
47
|
+
explanation: string;
|
|
48
|
+
}>;
|
|
49
|
+
/**
|
|
50
|
+
* Extract keywords from bug description for LSP filtering
|
|
51
|
+
*/
|
|
52
|
+
private extractKeywords;
|
|
53
|
+
/**
|
|
54
|
+
* Analyze bug and generate fix plan using Claude (two-phase approach)
|
|
55
|
+
* Phase 1: Send file list -> Claude picks relevant files
|
|
56
|
+
* Phase 2: Send those files -> Claude generates fix
|
|
57
|
+
*/
|
|
58
|
+
analyzeAndPlanFix(bug: BugReport, app: AppRegistryEntry, allFiles: string[], readFiles: (paths: string[]) => Promise<FileContent[]>, runLspAnalysis?: (files: string[], keywords: string[]) => Promise<{
|
|
59
|
+
unusedProps: {
|
|
60
|
+
name: string;
|
|
61
|
+
line: number;
|
|
62
|
+
file: string;
|
|
63
|
+
}[];
|
|
64
|
+
issues: {
|
|
65
|
+
file: string;
|
|
66
|
+
line: number;
|
|
67
|
+
message: string;
|
|
68
|
+
}[];
|
|
69
|
+
}>): Promise<FixPlan | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Retry fix based on apply error (search string not found) - re-reads file and generates new fix
|
|
72
|
+
*/
|
|
73
|
+
retryFixFromApplyError(bug: BugReport, previousFix: FixPlan, applyError: string, currentFiles: FileContent[], attempt: number): Promise<FixPlan | null>;
|
|
74
|
+
/**
|
|
75
|
+
* Retry fix based on build error - sends error to Claude to generate corrected fix
|
|
76
|
+
*/
|
|
77
|
+
retryFixFromError(bug: BugReport, previousFix: FixPlan, buildError: string, currentFiles: FileContent[], attempt: number): Promise<FixPlan | null>;
|
|
78
|
+
/**
|
|
79
|
+
* Generate a new fix based on user feedback
|
|
80
|
+
*/
|
|
81
|
+
retryFixWithFeedback(bug: BugReport, previousFix: FixPlan, feedback: string, allSourceFiles: string[], currentFiles: FileContent[]): Promise<FixPlan | null>;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=ai.d.ts.map
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Giuseppe AI Module - Claude API interactions for bug analysis and fix generation
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.GiuseppeAI = void 0;
|
|
10
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
11
|
+
const logger_1 = require("../../lib/logger");
|
|
12
|
+
const prompt_1 = require("./prompt");
|
|
13
|
+
const logger = (0, logger_1.createLogger)({ component: 'giuseppe-ai' });
|
|
14
|
+
class GiuseppeAI {
|
|
15
|
+
anthropic;
|
|
16
|
+
constructor(apiKey) {
|
|
17
|
+
if (apiKey) {
|
|
18
|
+
this.anthropic = new sdk_1.default({ apiKey });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
get isAvailable() {
|
|
22
|
+
return !!this.anthropic;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Classify a report as bug or feature request
|
|
26
|
+
*/
|
|
27
|
+
async classifyReport(bug) {
|
|
28
|
+
if (!this.anthropic) {
|
|
29
|
+
return { classification: 'unclear', reason: 'No AI available for classification' };
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const prompt = `Classify this report as either a BUG or a FEATURE REQUEST.
|
|
33
|
+
|
|
34
|
+
**Title:** ${bug.name}
|
|
35
|
+
**Description:** ${bug.description || 'No description'}
|
|
36
|
+
${bug.stepsToReproduce ? `**Steps:** ${bug.stepsToReproduce}` : ''}
|
|
37
|
+
${bug.expectedBehavior ? `**Expected:** ${bug.expectedBehavior}` : ''}
|
|
38
|
+
${bug.actualBehavior ? `**Actual:** ${bug.actualBehavior}` : ''}
|
|
39
|
+
|
|
40
|
+
A BUG is something BROKEN - it was working or should work but doesn't.
|
|
41
|
+
Examples: "button doesn't respond", "crashes when clicking", "data not saving", "display is wrong"
|
|
42
|
+
|
|
43
|
+
A FEATURE REQUEST is asking for something NEW or DIFFERENT - not broken, just wanted.
|
|
44
|
+
Examples: "change color to blue", "add dark mode", "make it bigger", "add new button"
|
|
45
|
+
|
|
46
|
+
Respond with JSON only:
|
|
47
|
+
{
|
|
48
|
+
"classification": "bug" | "feature_request" | "unclear",
|
|
49
|
+
"reason": "One sentence explaining why"
|
|
50
|
+
}`;
|
|
51
|
+
const response = await this.anthropic.messages.create({
|
|
52
|
+
model: 'claude-haiku-4-5-20251001',
|
|
53
|
+
max_tokens: 200,
|
|
54
|
+
messages: [{ role: 'user', content: prompt }]
|
|
55
|
+
});
|
|
56
|
+
const content = response.content[0];
|
|
57
|
+
if (content.type !== 'text') {
|
|
58
|
+
return { classification: 'unclear', reason: 'Could not classify' };
|
|
59
|
+
}
|
|
60
|
+
const parsed = JSON.parse(content.text.replace(/```json\n?|\n?```/g, ''));
|
|
61
|
+
return {
|
|
62
|
+
classification: parsed.classification || 'unclear',
|
|
63
|
+
reason: parsed.reason || 'No reason provided'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
logger.error('Classification failed', { error });
|
|
68
|
+
return { classification: 'unclear', reason: 'Classification error' };
|
|
69
|
+
}
|
|
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
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Analyze bug and generate fix plan using Claude (two-phase approach)
|
|
168
|
+
* Phase 1: Send file list -> Claude picks relevant files
|
|
169
|
+
* Phase 2: Send those files -> Claude generates fix
|
|
170
|
+
*/
|
|
171
|
+
async analyzeAndPlanFix(bug, app, allFiles, readFiles, runLspAnalysis) {
|
|
172
|
+
if (!this.anthropic) {
|
|
173
|
+
logger.warn('Anthropic client not initialized - cannot generate fix');
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
// Phase 1: Get file list and ask Claude which files to examine
|
|
178
|
+
logger.info('Phase 1: Asking Claude which files to examine', { fileCount: allFiles.length });
|
|
179
|
+
const phase1Prompt = `
|
|
180
|
+
Bug Report:
|
|
181
|
+
- Title: ${bug.name}
|
|
182
|
+
- Description: ${bug.description}
|
|
183
|
+
${bug.stepsToReproduce ? `- Steps to reproduce: ${bug.stepsToReproduce}` : ''}
|
|
184
|
+
${bug.expectedBehavior ? `- Expected: ${bug.expectedBehavior}` : ''}
|
|
185
|
+
${bug.actualBehavior ? `- Actual: ${bug.actualBehavior}` : ''}
|
|
186
|
+
|
|
187
|
+
App: ${app.name}
|
|
188
|
+
|
|
189
|
+
Source files in this project:
|
|
190
|
+
${allFiles.map(f => `- ${f}`).join('\n')}
|
|
191
|
+
|
|
192
|
+
Based on the bug description, which files should I examine to fix this bug?
|
|
193
|
+
Return ONLY a JSON array of file paths, maximum 10 files. Example: ["src/App.tsx", "src/components/Player.tsx"]`;
|
|
194
|
+
const phase1Response = await this.anthropic.messages.create({
|
|
195
|
+
model: 'claude-sonnet-4-20250514',
|
|
196
|
+
max_tokens: 1024,
|
|
197
|
+
system: 'You are a code analyst. Return only a JSON array of file paths that are most relevant to the bug. No explanation.',
|
|
198
|
+
messages: [{ role: 'user', content: phase1Prompt }]
|
|
199
|
+
});
|
|
200
|
+
const phase1Content = phase1Response.content[0];
|
|
201
|
+
if (phase1Content.type !== 'text')
|
|
202
|
+
return null;
|
|
203
|
+
// Parse file list from response
|
|
204
|
+
let relevantFiles = [];
|
|
205
|
+
try {
|
|
206
|
+
// Try to extract JSON array
|
|
207
|
+
const jsonMatch = phase1Content.text.match(/\[[\s\S]*?\]/);
|
|
208
|
+
if (jsonMatch) {
|
|
209
|
+
relevantFiles = JSON.parse(jsonMatch[0]);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
logger.warn('Could not parse file list from Phase 1', { response: phase1Content.text });
|
|
214
|
+
// Fallback: take first 5 files from src/
|
|
215
|
+
relevantFiles = allFiles.filter(f => f.startsWith('src/')).slice(0, 5);
|
|
216
|
+
}
|
|
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
|
+
}
|
|
231
|
+
// Phase 2: Read selected files and generate fix
|
|
232
|
+
const sourceFiles = await readFiles(relevantFiles);
|
|
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
|
+
}
|
|
257
|
+
const phase2Prompt = `
|
|
258
|
+
Bug Report:
|
|
259
|
+
- Title: ${bug.name}
|
|
260
|
+
- Description: ${bug.description}
|
|
261
|
+
${bug.stepsToReproduce ? `- Steps to reproduce: ${bug.stepsToReproduce}` : ''}
|
|
262
|
+
${bug.expectedBehavior ? `- Expected: ${bug.expectedBehavior}` : ''}
|
|
263
|
+
${bug.actualBehavior ? `- Actual: ${bug.actualBehavior}` : ''}
|
|
264
|
+
- Priority: ${bug.priority || 'unknown'}
|
|
265
|
+
${lspSection}
|
|
266
|
+
App: ${app.name}
|
|
267
|
+
Project path: ${app.projectPath}
|
|
268
|
+
|
|
269
|
+
Source files:
|
|
270
|
+
${sourceFiles.map(f => `--- ${f.path} ---\n${f.content}\n`).join('\n')}
|
|
271
|
+
|
|
272
|
+
Analyze this bug and provide a fix. Output JSON only.`;
|
|
273
|
+
const phase2Response = await this.anthropic.messages.create({
|
|
274
|
+
model: 'claude-sonnet-4-20250514',
|
|
275
|
+
max_tokens: 4096,
|
|
276
|
+
system: prompt_1.GIUSEPPE_SYSTEM_PROMPT,
|
|
277
|
+
messages: [{ role: 'user', content: phase2Prompt }]
|
|
278
|
+
});
|
|
279
|
+
// Extract JSON from response
|
|
280
|
+
const content = phase2Response.content[0];
|
|
281
|
+
if (content.type !== 'text')
|
|
282
|
+
return null;
|
|
283
|
+
const jsonMatch = content.text.match(/```json\n([\s\S]*?)\n```/);
|
|
284
|
+
if (!jsonMatch) {
|
|
285
|
+
// Try parsing whole response as JSON
|
|
286
|
+
try {
|
|
287
|
+
return JSON.parse(content.text);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
logger.warn('Could not parse fix plan from response');
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return JSON.parse(jsonMatch[1]);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
logger.error('Failed to analyze bug', { error });
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Retry fix based on apply error (search string not found) - re-reads file and generates new fix
|
|
303
|
+
*/
|
|
304
|
+
async retryFixFromApplyError(bug, previousFix, applyError, currentFiles, attempt) {
|
|
305
|
+
if (!this.anthropic)
|
|
306
|
+
return null;
|
|
307
|
+
try {
|
|
308
|
+
logger.info('Retrying fix after apply error', { attempt, error: applyError });
|
|
309
|
+
const retryPrompt = `
|
|
310
|
+
The previous fix attempt FAILED to apply with this error:
|
|
311
|
+
\`\`\`
|
|
312
|
+
${applyError}
|
|
313
|
+
\`\`\`
|
|
314
|
+
|
|
315
|
+
This usually means the search string didn't match the actual file content.
|
|
316
|
+
|
|
317
|
+
Previous fix that was attempted:
|
|
318
|
+
${JSON.stringify(previousFix.fix, null, 2)}
|
|
319
|
+
|
|
320
|
+
Bug Report:
|
|
321
|
+
- Title: ${bug.name}
|
|
322
|
+
- Description: ${bug.description}
|
|
323
|
+
|
|
324
|
+
CURRENT state of the files (read just now):
|
|
325
|
+
${currentFiles.map(f => `--- ${f.path} ---\n${f.content}\n`).join('\n')}
|
|
326
|
+
|
|
327
|
+
Please analyze the current file content and provide a CORRECTED fix.
|
|
328
|
+
Make sure the "search" strings EXACTLY match what's in the current files.
|
|
329
|
+
Output JSON only.`;
|
|
330
|
+
const response = await this.anthropic.messages.create({
|
|
331
|
+
model: 'claude-sonnet-4-20250514',
|
|
332
|
+
max_tokens: 4096,
|
|
333
|
+
system: prompt_1.GIUSEPPE_SYSTEM_PROMPT + '\n\nIMPORTANT: Your previous fix could not be applied because the search string was not found. Read the CURRENT file content carefully and make sure your search strings match EXACTLY.',
|
|
334
|
+
messages: [{ role: 'user', content: retryPrompt }]
|
|
335
|
+
});
|
|
336
|
+
const content = response.content[0];
|
|
337
|
+
if (content.type !== 'text')
|
|
338
|
+
return null;
|
|
339
|
+
const jsonMatch = content.text.match(/```json\n([\s\S]*?)\n```/);
|
|
340
|
+
if (!jsonMatch) {
|
|
341
|
+
try {
|
|
342
|
+
return JSON.parse(content.text);
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return JSON.parse(jsonMatch[1]);
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
logger.error('Failed to generate retry fix from apply error', { error });
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Retry fix based on build error - sends error to Claude to generate corrected fix
|
|
357
|
+
*/
|
|
358
|
+
async retryFixFromError(bug, previousFix, buildError, currentFiles, attempt) {
|
|
359
|
+
if (!this.anthropic)
|
|
360
|
+
return null;
|
|
361
|
+
try {
|
|
362
|
+
logger.info('Retrying fix based on build error', { attempt, error: buildError.substring(0, 200) });
|
|
363
|
+
const retryPrompt = `
|
|
364
|
+
The previous fix attempt FAILED with this build error:
|
|
365
|
+
\`\`\`
|
|
366
|
+
${buildError}
|
|
367
|
+
\`\`\`
|
|
368
|
+
|
|
369
|
+
Previous fix that was attempted:
|
|
370
|
+
${JSON.stringify(previousFix.fix, null, 2)}
|
|
371
|
+
|
|
372
|
+
Bug Report:
|
|
373
|
+
- Title: ${bug.name}
|
|
374
|
+
- Description: ${bug.description}
|
|
375
|
+
|
|
376
|
+
Current state of the files after the failed fix:
|
|
377
|
+
${currentFiles.map(f => `--- ${f.path} ---\n${f.content}\n`).join('\n')}
|
|
378
|
+
|
|
379
|
+
Please analyze the build error and provide a CORRECTED fix.
|
|
380
|
+
The fix must resolve the TypeScript/build error while still addressing the original bug.
|
|
381
|
+
Output JSON only.`;
|
|
382
|
+
const response = await this.anthropic.messages.create({
|
|
383
|
+
model: 'claude-sonnet-4-20250514',
|
|
384
|
+
max_tokens: 4096,
|
|
385
|
+
system: prompt_1.GIUSEPPE_SYSTEM_PROMPT + '\n\nIMPORTANT: Your previous fix caused a build error. Analyze the error carefully and provide a corrected fix that compiles successfully.',
|
|
386
|
+
messages: [{ role: 'user', content: retryPrompt }]
|
|
387
|
+
});
|
|
388
|
+
const content = response.content[0];
|
|
389
|
+
if (content.type !== 'text')
|
|
390
|
+
return null;
|
|
391
|
+
const jsonMatch = content.text.match(/```json\n([\s\S]*?)\n```/);
|
|
392
|
+
if (!jsonMatch) {
|
|
393
|
+
try {
|
|
394
|
+
return JSON.parse(content.text);
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return JSON.parse(jsonMatch[1]);
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
logger.error('Failed to generate retry fix', { error });
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Generate a new fix based on user feedback
|
|
409
|
+
*/
|
|
410
|
+
async retryFixWithFeedback(bug, previousFix, feedback, allSourceFiles, currentFiles) {
|
|
411
|
+
if (!this.anthropic)
|
|
412
|
+
return null;
|
|
413
|
+
try {
|
|
414
|
+
const prompt = `
|
|
415
|
+
The previous fix was REJECTED by the user. Here's their feedback:
|
|
416
|
+
|
|
417
|
+
**User feedback:**
|
|
418
|
+
${feedback}
|
|
419
|
+
|
|
420
|
+
**Original bug:**
|
|
421
|
+
- Title: ${bug.name}
|
|
422
|
+
- Description: ${bug.description}
|
|
423
|
+
${bug.stepsToReproduce ? `- Steps to reproduce: ${bug.stepsToReproduce}` : ''}
|
|
424
|
+
${bug.expectedBehavior ? `- Expected: ${bug.expectedBehavior}` : ''}
|
|
425
|
+
${bug.actualBehavior ? `- Actual: ${bug.actualBehavior}` : ''}
|
|
426
|
+
|
|
427
|
+
**Previous fix that was rejected:**
|
|
428
|
+
${JSON.stringify(previousFix.fix, null, 2)}
|
|
429
|
+
|
|
430
|
+
**AVAILABLE SOURCE FILES (use ONLY these exact paths):**
|
|
431
|
+
${allSourceFiles.map(f => `- ${f}`).join('\n')}
|
|
432
|
+
|
|
433
|
+
**Relevant source files (searched based on your feedback):**
|
|
434
|
+
${currentFiles.map(f => `--- ${f.path} ---\n${f.content}\n`).join('\n')}
|
|
435
|
+
|
|
436
|
+
Based on the user's feedback, generate a DIFFERENT fix that addresses their concerns.
|
|
437
|
+
IMPORTANT: Use ONLY file paths from the AVAILABLE SOURCE FILES list above.
|
|
438
|
+
Output JSON only.`;
|
|
439
|
+
const response = await this.anthropic.messages.create({
|
|
440
|
+
model: 'claude-sonnet-4-20250514',
|
|
441
|
+
max_tokens: 4096,
|
|
442
|
+
system: prompt_1.GIUSEPPE_SYSTEM_PROMPT + '\n\nIMPORTANT: The user rejected the previous fix. Read their feedback carefully and try a different approach.',
|
|
443
|
+
messages: [{ role: 'user', content: prompt }]
|
|
444
|
+
});
|
|
445
|
+
const content = response.content[0];
|
|
446
|
+
if (content.type !== 'text')
|
|
447
|
+
return null;
|
|
448
|
+
const jsonMatch = content.text.match(/```json\n([\s\S]*?)\n```/);
|
|
449
|
+
if (!jsonMatch) {
|
|
450
|
+
try {
|
|
451
|
+
return JSON.parse(content.text);
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return JSON.parse(jsonMatch[1]);
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
logger.error('Failed to generate retry fix with feedback', { error });
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
exports.GiuseppeAI = GiuseppeAI;
|
|
466
|
+
//# sourceMappingURL=ai.js.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug Reports Module - Giuseppe Bot
|
|
3
|
+
*
|
|
4
|
+
* Autonomous app fixer that:
|
|
5
|
+
* 1. Analyzes bug reports
|
|
6
|
+
* 2. Finds app project code
|
|
7
|
+
* 3. Generates fixes using Claude
|
|
8
|
+
* 4. Tests locally
|
|
9
|
+
* 5. Publishes to production
|
|
10
|
+
*/
|
|
11
|
+
import type { UserContext } from '../../mcp/UserContextCache';
|
|
12
|
+
import type { BugReport, BugReportsConfig, FixResult } from './types';
|
|
13
|
+
import type { BugMonitor } from './monitor';
|
|
14
|
+
export declare class GiuseppeBot {
|
|
15
|
+
private userContext;
|
|
16
|
+
private config;
|
|
17
|
+
private monitor;
|
|
18
|
+
private ai;
|
|
19
|
+
private git;
|
|
20
|
+
private files;
|
|
21
|
+
private lsp;
|
|
22
|
+
private appsRegistry;
|
|
23
|
+
constructor(userContext: UserContext, config: BugReportsConfig, monitor: BugMonitor);
|
|
24
|
+
/**
|
|
25
|
+
* Create a session log entry in Context Log workflow
|
|
26
|
+
* Links to the bug report for traceability
|
|
27
|
+
*/
|
|
28
|
+
private createSessionLogEntry;
|
|
29
|
+
/**
|
|
30
|
+
* Check if this bug was already classified by looking for classification message in discussion
|
|
31
|
+
*/
|
|
32
|
+
private wasAlreadyClassified;
|
|
33
|
+
/**
|
|
34
|
+
* Handle a new bug - main entry point
|
|
35
|
+
*/
|
|
36
|
+
handleBug(bug: BugReport): Promise<FixResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Continue with bug fix after user confirms
|
|
39
|
+
*/
|
|
40
|
+
proceedWithFix(bug: BugReport): Promise<FixResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Handle a message in a bug discussion - state machine for approval flow
|
|
43
|
+
*/
|
|
44
|
+
handleDiscussionMessage(discussionId: string, message: string, senderId: string): Promise<{
|
|
45
|
+
approved: boolean;
|
|
46
|
+
published: boolean;
|
|
47
|
+
retrying?: boolean;
|
|
48
|
+
error?: string;
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Handle "fix it" confirmation - proceed with bug fix
|
|
52
|
+
*/
|
|
53
|
+
private handleFixItConfirmation;
|
|
54
|
+
/**
|
|
55
|
+
* Handle "not a bug" confirmation - close without fixing
|
|
56
|
+
*/
|
|
57
|
+
private handleNotABugConfirmation;
|
|
58
|
+
/**
|
|
59
|
+
* Handle "approved" - publish to production
|
|
60
|
+
*/
|
|
61
|
+
private handleApproval;
|
|
62
|
+
/**
|
|
63
|
+
* Handle "denied" - ask for explanation
|
|
64
|
+
*/
|
|
65
|
+
private handleDenial;
|
|
66
|
+
/**
|
|
67
|
+
* Handle explanation after denial - retry the fix
|
|
68
|
+
*/
|
|
69
|
+
private handleExplanationAndRetry;
|
|
70
|
+
/**
|
|
71
|
+
* Check if there's a pending fix for a discussion
|
|
72
|
+
*/
|
|
73
|
+
hasPendingFix(discussionId: string): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Retry a pending fix with explanation from HAL
|
|
76
|
+
* Called by HAL after gathering info through conversation
|
|
77
|
+
*/
|
|
78
|
+
retryWithExplanation(discussionId: string, explanation: string): Promise<boolean>;
|
|
79
|
+
/**
|
|
80
|
+
* Initialize registry callback (call this after construction)
|
|
81
|
+
*/
|
|
82
|
+
initializeRegistryCallback(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Initialize classification registry callbacks for HAL coordination
|
|
85
|
+
* This allows HAL to trigger "fix it" or "not a bug" via natural conversation
|
|
86
|
+
*/
|
|
87
|
+
initializeClassificationCallbacks(): void;
|
|
88
|
+
/**
|
|
89
|
+
* Helper to send a message to a discussion
|
|
90
|
+
*/
|
|
91
|
+
private sendDiscussionMessage;
|
|
92
|
+
/**
|
|
93
|
+
* Build and test the app
|
|
94
|
+
*/
|
|
95
|
+
private buildAndTest;
|
|
96
|
+
/**
|
|
97
|
+
* Join a bug's discussion using the activity ID (not discussion ID)
|
|
98
|
+
* For activity discussions, we must use joinActivityDiscussion
|
|
99
|
+
*/
|
|
100
|
+
private joinBugDiscussion;
|
|
101
|
+
/**
|
|
102
|
+
* Publish app to production using expect script (same as MCP tool)
|
|
103
|
+
*/
|
|
104
|
+
private publishApp;
|
|
105
|
+
/**
|
|
106
|
+
* Report progress to bug discussion
|
|
107
|
+
*/
|
|
108
|
+
private reportProgress;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=bot.d.ts.map
|