@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
package/dist/mcp-server.js
CHANGED
|
@@ -49,6 +49,7 @@ const logger_1 = require("./lib/logger");
|
|
|
49
49
|
const config_1 = require("./config");
|
|
50
50
|
const UserContextCache_1 = require("./mcp/UserContextCache");
|
|
51
51
|
const tool_registry_1 = require("./mcp/tool-registry");
|
|
52
|
+
const webhook_handler_1 = require("./mcp/webhook-handler");
|
|
52
53
|
class MCPServerService {
|
|
53
54
|
app;
|
|
54
55
|
server;
|
|
@@ -325,12 +326,61 @@ class MCPServerService {
|
|
|
325
326
|
res.status(500).json({ error: 'Failed to toggle bot' });
|
|
326
327
|
}
|
|
327
328
|
});
|
|
328
|
-
|
|
329
|
+
// ===== Bot Config Webhook API =====
|
|
330
|
+
// Get secure webhook path (auto-generated token)
|
|
331
|
+
const webhookPath = (0, webhook_handler_1.getWebhookPath)();
|
|
332
|
+
// POST /webhook/{token} - Receives updates from Hailer workflow webhooks
|
|
333
|
+
this.app.post(webhookPath, (req, res) => {
|
|
334
|
+
req.logger.info('Bot config webhook received', {
|
|
335
|
+
activityId: req.body?._id,
|
|
336
|
+
activityName: req.body?.name,
|
|
337
|
+
workspaceId: req.body?.cid,
|
|
338
|
+
});
|
|
339
|
+
try {
|
|
340
|
+
const result = (0, webhook_handler_1.handleBotConfigWebhook)(req.body);
|
|
341
|
+
if (result.success) {
|
|
342
|
+
req.logger.info('Bot config updated via webhook', {
|
|
343
|
+
action: result.action,
|
|
344
|
+
workspaceId: result.workspaceId,
|
|
345
|
+
botType: result.botType,
|
|
346
|
+
});
|
|
347
|
+
res.status(200).json(result);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
req.logger.warn('Bot config webhook failed', { error: result.error });
|
|
351
|
+
res.status(400).json(result);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
req.logger.error('Bot config webhook error', { error });
|
|
356
|
+
res.status(500).json({
|
|
357
|
+
success: false,
|
|
358
|
+
error: error instanceof Error ? error.message : 'Internal error',
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
// GET /webhook/{token}/status - Status endpoint to see all workspace configs
|
|
363
|
+
this.app.get(`${webhookPath}/status`, (_req, res) => {
|
|
364
|
+
const configs = (0, webhook_handler_1.listWorkspaceConfigs)();
|
|
365
|
+
res.json({
|
|
366
|
+
timestamp: new Date().toISOString(),
|
|
367
|
+
workspaceCount: configs.length,
|
|
368
|
+
workspaces: configs.map((c) => ({
|
|
369
|
+
workspaceId: c.workspaceId,
|
|
370
|
+
workspaceName: c.workspaceName,
|
|
371
|
+
hasOrchestrator: !!c.orchestrator,
|
|
372
|
+
specialistCount: c.specialists.length,
|
|
373
|
+
enabledSpecialists: c.specialists.filter((s) => s.enabled).length,
|
|
374
|
+
lastSynced: c.lastSynced,
|
|
375
|
+
})),
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
this.logger.debug('Routes configured', {
|
|
329
379
|
routes: [
|
|
330
|
-
'/health
|
|
331
|
-
'/daemon/status
|
|
332
|
-
'/api/mcp
|
|
333
|
-
'/api/bots
|
|
380
|
+
'/health',
|
|
381
|
+
'/daemon/status',
|
|
382
|
+
'/api/mcp',
|
|
383
|
+
'/api/bots'
|
|
334
384
|
]
|
|
335
385
|
});
|
|
336
386
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Giuseppe Agent - Full Conversational Bug Fixer
|
|
3
|
+
*
|
|
4
|
+
* A natural language agent that handles bug fixing through conversation.
|
|
5
|
+
* No keywords, no intent detection - just natural dialogue with tools.
|
|
6
|
+
*/
|
|
7
|
+
import type { UserContext } from '../../mcp/UserContextCache';
|
|
8
|
+
import type { BugReport } from './types';
|
|
9
|
+
import type { BugMonitor } from './bug-monitor';
|
|
10
|
+
export declare class GiuseppeAgent {
|
|
11
|
+
private userContext;
|
|
12
|
+
private monitor;
|
|
13
|
+
private appsBasePath;
|
|
14
|
+
private anthropic;
|
|
15
|
+
private conversations;
|
|
16
|
+
private bugContexts;
|
|
17
|
+
private files;
|
|
18
|
+
private git;
|
|
19
|
+
private lsp;
|
|
20
|
+
constructor(userContext: UserContext, monitor: BugMonitor, appsBasePath: string, apiKey?: string);
|
|
21
|
+
/**
|
|
22
|
+
* Handle a new bug - start the conversation
|
|
23
|
+
*/
|
|
24
|
+
handleNewBug(bug: BugReport): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Handle any message in the bug discussion
|
|
27
|
+
*/
|
|
28
|
+
handleMessage(discussionId: string, message: string, senderName: string): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Chat with the LLM - handles tool calls automatically
|
|
31
|
+
*/
|
|
32
|
+
private chat;
|
|
33
|
+
/**
|
|
34
|
+
* Execute tool calls and return results
|
|
35
|
+
*/
|
|
36
|
+
private executeToolCalls;
|
|
37
|
+
/**
|
|
38
|
+
* Execute a single tool
|
|
39
|
+
*/
|
|
40
|
+
private executeTool;
|
|
41
|
+
/**
|
|
42
|
+
* Post message to discussion
|
|
43
|
+
*/
|
|
44
|
+
private postMessage;
|
|
45
|
+
/**
|
|
46
|
+
* Build initial prompt with bug context
|
|
47
|
+
*/
|
|
48
|
+
private buildInitialPrompt;
|
|
49
|
+
/**
|
|
50
|
+
* System prompt for natural conversation
|
|
51
|
+
*/
|
|
52
|
+
private getSystemPrompt;
|
|
53
|
+
/**
|
|
54
|
+
* Tool definitions
|
|
55
|
+
*/
|
|
56
|
+
private getTools;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=giuseppe-agent.d.ts.map
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Giuseppe Agent - Full Conversational Bug Fixer
|
|
4
|
+
*
|
|
5
|
+
* A natural language agent that handles bug fixing through conversation.
|
|
6
|
+
* No keywords, no intent detection - just natural dialogue with tools.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.GiuseppeAgent = void 0;
|
|
46
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
47
|
+
const child_process_1 = require("child_process");
|
|
48
|
+
const logger_1 = require("../../lib/logger");
|
|
49
|
+
const giuseppe_files_1 = require("./giuseppe-files");
|
|
50
|
+
const giuseppe_git_1 = require("./giuseppe-git");
|
|
51
|
+
const giuseppe_lsp_1 = require("./giuseppe-lsp");
|
|
52
|
+
const logger = (0, logger_1.createLogger)({ component: 'giuseppe-agent' });
|
|
53
|
+
class GiuseppeAgent {
|
|
54
|
+
userContext;
|
|
55
|
+
monitor;
|
|
56
|
+
appsBasePath;
|
|
57
|
+
anthropic;
|
|
58
|
+
conversations = new Map();
|
|
59
|
+
bugContexts = new Map();
|
|
60
|
+
// Sub-modules
|
|
61
|
+
files;
|
|
62
|
+
git;
|
|
63
|
+
lsp;
|
|
64
|
+
constructor(userContext, monitor, appsBasePath, apiKey) {
|
|
65
|
+
this.userContext = userContext;
|
|
66
|
+
this.monitor = monitor;
|
|
67
|
+
this.appsBasePath = appsBasePath;
|
|
68
|
+
this.anthropic = new sdk_1.default({ apiKey: apiKey || process.env.ANTHROPIC_API_KEY });
|
|
69
|
+
this.files = new giuseppe_files_1.GiuseppeFiles(appsBasePath);
|
|
70
|
+
this.git = new giuseppe_git_1.GiuseppeGit();
|
|
71
|
+
this.lsp = new giuseppe_lsp_1.GiuseppeLsp();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Handle a new bug - start the conversation
|
|
75
|
+
*/
|
|
76
|
+
async handleNewBug(bug) {
|
|
77
|
+
if (!bug.discussionId) {
|
|
78
|
+
logger.warn('Bug has no discussion, cannot start conversation', { bugId: bug.id });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Store bug context
|
|
82
|
+
this.bugContexts.set(bug.discussionId, { bug });
|
|
83
|
+
// Find the app
|
|
84
|
+
const app = await this.files.findAppFromBugTitle(bug.name);
|
|
85
|
+
if (app) {
|
|
86
|
+
this.bugContexts.get(bug.discussionId).app = app;
|
|
87
|
+
}
|
|
88
|
+
// Start fresh conversation
|
|
89
|
+
this.conversations.set(bug.discussionId, []);
|
|
90
|
+
// Send initial message with bug context
|
|
91
|
+
const initialPrompt = this.buildInitialPrompt(bug, app);
|
|
92
|
+
const response = await this.chat(bug.discussionId, initialPrompt, true);
|
|
93
|
+
// Post response to discussion
|
|
94
|
+
await this.postMessage(bug.discussionId, response);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Handle any message in the bug discussion
|
|
98
|
+
*/
|
|
99
|
+
async handleMessage(discussionId, message, senderName) {
|
|
100
|
+
const context = this.bugContexts.get(discussionId);
|
|
101
|
+
if (!context) {
|
|
102
|
+
logger.debug('No bug context for discussion, ignoring', { discussionId });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// Add user message to conversation
|
|
106
|
+
const userMessage = `${senderName}: ${message}`;
|
|
107
|
+
const response = await this.chat(discussionId, userMessage, false);
|
|
108
|
+
// Post response
|
|
109
|
+
await this.postMessage(discussionId, response);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Chat with the LLM - handles tool calls automatically
|
|
113
|
+
*/
|
|
114
|
+
async chat(discussionId, userMessage, isSystemContext) {
|
|
115
|
+
const conversation = this.conversations.get(discussionId) || [];
|
|
116
|
+
// Add user message
|
|
117
|
+
conversation.push({ role: 'user', content: userMessage });
|
|
118
|
+
// Keep conversation manageable
|
|
119
|
+
if (conversation.length > 30) {
|
|
120
|
+
conversation.splice(0, conversation.length - 20);
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const response = await this.anthropic.messages.create({
|
|
124
|
+
model: 'claude-sonnet-4-20250514',
|
|
125
|
+
max_tokens: 4096,
|
|
126
|
+
system: this.getSystemPrompt(),
|
|
127
|
+
messages: conversation,
|
|
128
|
+
tools: this.getTools(),
|
|
129
|
+
});
|
|
130
|
+
// Handle tool calls
|
|
131
|
+
let finalResponse = response;
|
|
132
|
+
while (finalResponse.stop_reason === 'tool_use') {
|
|
133
|
+
const toolResults = await this.executeToolCalls(finalResponse.content, discussionId);
|
|
134
|
+
// Add assistant response and tool results
|
|
135
|
+
conversation.push({ role: 'assistant', content: finalResponse.content });
|
|
136
|
+
conversation.push({ role: 'user', content: toolResults });
|
|
137
|
+
// Continue conversation
|
|
138
|
+
finalResponse = await this.anthropic.messages.create({
|
|
139
|
+
model: 'claude-sonnet-4-20250514',
|
|
140
|
+
max_tokens: 4096,
|
|
141
|
+
system: this.getSystemPrompt(),
|
|
142
|
+
messages: conversation,
|
|
143
|
+
tools: this.getTools(),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// Extract text response
|
|
147
|
+
const textContent = finalResponse.content
|
|
148
|
+
.filter((block) => block.type === 'text')
|
|
149
|
+
.map(block => block.text)
|
|
150
|
+
.join('\n');
|
|
151
|
+
// Save final response
|
|
152
|
+
conversation.push({ role: 'assistant', content: finalResponse.content });
|
|
153
|
+
this.conversations.set(discussionId, conversation);
|
|
154
|
+
return textContent || "I'm not sure how to respond to that.";
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
logger.error('Chat failed', { error, discussionId });
|
|
158
|
+
return "Sorry, I encountered an error. Please try again.";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Execute tool calls and return results
|
|
163
|
+
*/
|
|
164
|
+
async executeToolCalls(content, discussionId) {
|
|
165
|
+
const toolUses = content.filter((block) => block.type === 'tool_use');
|
|
166
|
+
const results = [];
|
|
167
|
+
const context = this.bugContexts.get(discussionId);
|
|
168
|
+
for (const tool of toolUses) {
|
|
169
|
+
try {
|
|
170
|
+
const result = await this.executeTool(tool.name, tool.input, context);
|
|
171
|
+
results.push({
|
|
172
|
+
type: 'tool_result',
|
|
173
|
+
tool_use_id: tool.id,
|
|
174
|
+
content: JSON.stringify(result),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
results.push({
|
|
179
|
+
type: 'tool_result',
|
|
180
|
+
tool_use_id: tool.id,
|
|
181
|
+
content: JSON.stringify({ error: error.message }),
|
|
182
|
+
is_error: true,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return results;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Execute a single tool
|
|
190
|
+
*/
|
|
191
|
+
async executeTool(name, input, context) {
|
|
192
|
+
const projectPath = context?.app?.projectPath || input.projectPath;
|
|
193
|
+
switch (name) {
|
|
194
|
+
case 'check_git_status': {
|
|
195
|
+
try {
|
|
196
|
+
const status = (0, child_process_1.execSync)('git status --short', { cwd: projectPath, encoding: 'utf-8' });
|
|
197
|
+
const branch = (0, child_process_1.execSync)('git branch --show-current', { cwd: projectPath, encoding: 'utf-8' }).trim();
|
|
198
|
+
const behindAhead = (0, child_process_1.execSync)('git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null || echo "0 0"', { cwd: projectPath, encoding: 'utf-8' }).trim().split(/\s+/);
|
|
199
|
+
return {
|
|
200
|
+
branch,
|
|
201
|
+
status: status || 'clean',
|
|
202
|
+
behind: parseInt(behindAhead[1]) || 0,
|
|
203
|
+
ahead: parseInt(behindAhead[0]) || 0
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
return { error: e.message };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
case 'git_pull': {
|
|
211
|
+
try {
|
|
212
|
+
const output = (0, child_process_1.execSync)('git pull', { cwd: projectPath, encoding: 'utf-8' });
|
|
213
|
+
return { success: true, output };
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
return { success: false, error: e.stderr || e.message };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
case 'git_commit': {
|
|
220
|
+
try {
|
|
221
|
+
(0, child_process_1.execSync)('git add -A', { cwd: projectPath });
|
|
222
|
+
const msg = input.message || 'fix: bug fix by Giuseppe';
|
|
223
|
+
(0, child_process_1.execSync)(`git commit -m "${msg.replace(/"/g, '\\"')}"`, { cwd: projectPath });
|
|
224
|
+
const hash = (0, child_process_1.execSync)('git rev-parse --short HEAD', { cwd: projectPath, encoding: 'utf-8' }).trim();
|
|
225
|
+
return { success: true, hash };
|
|
226
|
+
}
|
|
227
|
+
catch (e) {
|
|
228
|
+
return { success: false, error: e.stderr || e.stdout || e.message };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
case 'git_push': {
|
|
232
|
+
const pushed = await this.git.push(projectPath);
|
|
233
|
+
return { success: pushed };
|
|
234
|
+
}
|
|
235
|
+
case 'git_revert': {
|
|
236
|
+
try {
|
|
237
|
+
(0, child_process_1.execSync)('git checkout -- .', { cwd: projectPath });
|
|
238
|
+
return { success: true };
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
return { success: false, error: e.message };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
case 'list_files': {
|
|
245
|
+
const files = await this.files.scanSourceFiles(projectPath);
|
|
246
|
+
return { files: files.slice(0, 50), total: files.length };
|
|
247
|
+
}
|
|
248
|
+
case 'read_file': {
|
|
249
|
+
const content = await this.files.readFile(projectPath, input.path);
|
|
250
|
+
return content ? { content } : { error: 'File not found' };
|
|
251
|
+
}
|
|
252
|
+
case 'write_file': {
|
|
253
|
+
const success = await this.files.writeFile(projectPath, input.path, input.content);
|
|
254
|
+
return { success };
|
|
255
|
+
}
|
|
256
|
+
case 'run_build': {
|
|
257
|
+
const result = await this.lsp.validateFix(projectPath);
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
case 'analyze_code': {
|
|
261
|
+
const analysis = await this.lsp.analyzeProject(projectPath);
|
|
262
|
+
return {
|
|
263
|
+
issues: analysis.issues.slice(0, 10),
|
|
264
|
+
unusedProps: analysis.unusedProps.slice(0, 10),
|
|
265
|
+
summary: analysis.summary
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
case 'publish_app': {
|
|
269
|
+
try {
|
|
270
|
+
const appScaffold = await Promise.resolve().then(() => __importStar(require('../../mcp/tools/app-scaffold')));
|
|
271
|
+
const tool = appScaffold.publishHailerAppTool;
|
|
272
|
+
const result = await tool.execute({ projectDirectory: projectPath }, this.userContext);
|
|
273
|
+
const text = result.content?.[0]?.text || '';
|
|
274
|
+
return {
|
|
275
|
+
success: !text.includes('❌'),
|
|
276
|
+
message: text.substring(0, 500)
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
return { success: false, error: e.message };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
case 'move_bug_phase': {
|
|
284
|
+
const phase = input.phase;
|
|
285
|
+
const bugId = context?.bug.id || input.bugId;
|
|
286
|
+
const moved = await this.monitor.moveBugToPhase(bugId, phase);
|
|
287
|
+
return { success: moved, phase };
|
|
288
|
+
}
|
|
289
|
+
default:
|
|
290
|
+
return { error: `Unknown tool: ${name}` };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Post message to discussion
|
|
295
|
+
*/
|
|
296
|
+
async postMessage(discussionId, message) {
|
|
297
|
+
try {
|
|
298
|
+
await this.userContext.hailer.sendDiscussionMessage(discussionId, message);
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
logger.error('Failed to post message', { discussionId, error });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Build initial prompt with bug context
|
|
306
|
+
*/
|
|
307
|
+
buildInitialPrompt(bug, app) {
|
|
308
|
+
return `A new bug report has been filed. Here are the details:
|
|
309
|
+
|
|
310
|
+
**Bug Report**
|
|
311
|
+
- Title: ${bug.name}
|
|
312
|
+
- Description: ${bug.description || 'No description provided'}
|
|
313
|
+
${bug.stepsToReproduce ? `- Steps to reproduce: ${bug.stepsToReproduce}` : ''}
|
|
314
|
+
${bug.expectedBehavior ? `- Expected: ${bug.expectedBehavior}` : ''}
|
|
315
|
+
${bug.actualBehavior ? `- Actual: ${bug.actualBehavior}` : ''}
|
|
316
|
+
|
|
317
|
+
**App Info**
|
|
318
|
+
${app ? `- Found app: ${app.name} at ${app.projectPath}` : '- Could not find matching app project'}
|
|
319
|
+
|
|
320
|
+
Please introduce yourself, analyze this bug report, and let the user know what you think. Ask any clarifying questions if needed.`;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* System prompt for natural conversation
|
|
324
|
+
*/
|
|
325
|
+
getSystemPrompt() {
|
|
326
|
+
return `You are Giuseppe, a friendly AI assistant specialized in fixing bugs in React/TypeScript applications.
|
|
327
|
+
|
|
328
|
+
**Your personality:**
|
|
329
|
+
- Friendly and conversational
|
|
330
|
+
- Technical but explains things clearly
|
|
331
|
+
- Proactive - you take initiative but ask before destructive actions
|
|
332
|
+
- Honest about limitations and errors
|
|
333
|
+
|
|
334
|
+
**Your workflow:**
|
|
335
|
+
1. When a bug is reported, analyze it and share your understanding
|
|
336
|
+
2. Ask clarifying questions if the bug is unclear
|
|
337
|
+
3. Check git status before making changes
|
|
338
|
+
4. If git is behind, pull first
|
|
339
|
+
5. Read relevant files to understand the code
|
|
340
|
+
6. Make fixes by writing to files
|
|
341
|
+
7. Run the build to verify the fix works
|
|
342
|
+
8. Commit the changes
|
|
343
|
+
9. Ask the user if they want to publish to production
|
|
344
|
+
10. Only publish when the user confirms
|
|
345
|
+
|
|
346
|
+
**Important rules:**
|
|
347
|
+
- ALWAYS ask before publishing to production
|
|
348
|
+
- If the build fails, explain the error and try to fix it
|
|
349
|
+
- If git has issues, explain and resolve them
|
|
350
|
+
- Keep the user informed of what you're doing
|
|
351
|
+
- Be conversational - this is a chat, not a report
|
|
352
|
+
|
|
353
|
+
**Tools available:**
|
|
354
|
+
- check_git_status: See branch, changes, sync status
|
|
355
|
+
- git_pull: Pull latest from remote
|
|
356
|
+
- git_commit: Stage and commit changes
|
|
357
|
+
- git_push: Push to remote
|
|
358
|
+
- git_revert: Discard uncommitted changes
|
|
359
|
+
- list_files: See source files in the project
|
|
360
|
+
- read_file: Read a file's contents
|
|
361
|
+
- write_file: Write/update a file
|
|
362
|
+
- run_build: Check if code compiles
|
|
363
|
+
- analyze_code: LSP analysis for issues
|
|
364
|
+
- publish_app: Deploy to Hailer (ASK FIRST!)
|
|
365
|
+
- move_bug_phase: Update bug status (inProgress, fixed, closed, declined)
|
|
366
|
+
|
|
367
|
+
Start by understanding the bug, then work through fixing it naturally.`;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Tool definitions
|
|
371
|
+
*/
|
|
372
|
+
getTools() {
|
|
373
|
+
return [
|
|
374
|
+
{
|
|
375
|
+
name: 'check_git_status',
|
|
376
|
+
description: 'Check git status - branch, changes, sync with remote',
|
|
377
|
+
input_schema: { type: 'object', properties: {}, required: [] }
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: 'git_pull',
|
|
381
|
+
description: 'Pull latest changes from remote',
|
|
382
|
+
input_schema: { type: 'object', properties: {}, required: [] }
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: 'git_commit',
|
|
386
|
+
description: 'Stage all changes and commit',
|
|
387
|
+
input_schema: {
|
|
388
|
+
type: 'object',
|
|
389
|
+
properties: {
|
|
390
|
+
message: { type: 'string', description: 'Commit message' }
|
|
391
|
+
},
|
|
392
|
+
required: ['message']
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: 'git_push',
|
|
397
|
+
description: 'Push commits to remote',
|
|
398
|
+
input_schema: { type: 'object', properties: {}, required: [] }
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
name: 'git_revert',
|
|
402
|
+
description: 'Discard all uncommitted changes',
|
|
403
|
+
input_schema: { type: 'object', properties: {}, required: [] }
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: 'list_files',
|
|
407
|
+
description: 'List source files in the project',
|
|
408
|
+
input_schema: { type: 'object', properties: {}, required: [] }
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: 'read_file',
|
|
412
|
+
description: 'Read a source file',
|
|
413
|
+
input_schema: {
|
|
414
|
+
type: 'object',
|
|
415
|
+
properties: {
|
|
416
|
+
path: { type: 'string', description: 'Relative path to file' }
|
|
417
|
+
},
|
|
418
|
+
required: ['path']
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
name: 'write_file',
|
|
423
|
+
description: 'Write content to a file',
|
|
424
|
+
input_schema: {
|
|
425
|
+
type: 'object',
|
|
426
|
+
properties: {
|
|
427
|
+
path: { type: 'string', description: 'Relative path to file' },
|
|
428
|
+
content: { type: 'string', description: 'New file content' }
|
|
429
|
+
},
|
|
430
|
+
required: ['path', 'content']
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
name: 'run_build',
|
|
435
|
+
description: 'Run TypeScript build to check for errors',
|
|
436
|
+
input_schema: { type: 'object', properties: {}, required: [] }
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
name: 'analyze_code',
|
|
440
|
+
description: 'Run LSP analysis to find issues',
|
|
441
|
+
input_schema: { type: 'object', properties: {}, required: [] }
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
name: 'publish_app',
|
|
445
|
+
description: 'Publish app to Hailer production - ALWAYS ASK USER FIRST',
|
|
446
|
+
input_schema: { type: 'object', properties: {}, required: [] }
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
name: 'move_bug_phase',
|
|
450
|
+
description: 'Move bug to a different phase',
|
|
451
|
+
input_schema: {
|
|
452
|
+
type: 'object',
|
|
453
|
+
properties: {
|
|
454
|
+
phase: {
|
|
455
|
+
type: 'string',
|
|
456
|
+
enum: ['inProgress', 'fixed', 'closed', 'declined'],
|
|
457
|
+
description: 'Target phase'
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
required: ['phase']
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
];
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
exports.GiuseppeAgent = GiuseppeAgent;
|
|
467
|
+
//# sourceMappingURL=giuseppe-agent.js.map
|
|
@@ -37,12 +37,36 @@ export declare class GiuseppeAI {
|
|
|
37
37
|
* Classify a report as bug or feature request
|
|
38
38
|
*/
|
|
39
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;
|
|
40
53
|
/**
|
|
41
54
|
* Analyze bug and generate fix plan using Claude (two-phase approach)
|
|
42
55
|
* Phase 1: Send file list -> Claude picks relevant files
|
|
43
56
|
* Phase 2: Send those files -> Claude generates fix
|
|
44
57
|
*/
|
|
45
|
-
analyzeAndPlanFix(bug: BugReport, app: AppRegistryEntry, allFiles: string[], readFiles: (paths: string[]) => Promise<FileContent[]
|
|
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>;
|
|
46
70
|
/**
|
|
47
71
|
* Retry fix based on apply error (search string not found) - re-reads file and generates new fix
|
|
48
72
|
*/
|