@hailer/mcp 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/.claude/settings.json +12 -0
  2. package/CLAUDE.md +37 -1
  3. package/dist/app.js +5 -0
  4. package/dist/client/agents/base.d.ts +5 -0
  5. package/dist/client/agents/base.js +9 -2
  6. package/dist/client/agents/definitions.js +85 -0
  7. package/dist/client/agents/orchestrator.d.ts +21 -0
  8. package/dist/client/agents/orchestrator.js +292 -1
  9. package/dist/client/bot-entrypoint.d.ts +7 -0
  10. package/dist/client/bot-entrypoint.js +103 -0
  11. package/dist/client/bot-runner.d.ts +35 -0
  12. package/dist/client/bot-runner.js +188 -0
  13. package/dist/client/factory.d.ts +4 -0
  14. package/dist/client/factory.js +10 -0
  15. package/dist/client/server.d.ts +8 -0
  16. package/dist/client/server.js +251 -0
  17. package/dist/client/types.d.ts +29 -0
  18. package/dist/client/types.js +4 -1
  19. package/dist/core.d.ts +3 -0
  20. package/dist/core.js +72 -0
  21. package/dist/mcp/hailer-clients.d.ts +4 -0
  22. package/dist/mcp/hailer-clients.js +16 -1
  23. package/dist/mcp/tools/app-scaffold.js +127 -5
  24. package/dist/mcp/tools/bot-config.d.ts +78 -0
  25. package/dist/mcp/tools/bot-config.js +442 -0
  26. package/dist/mcp-server.js +109 -1
  27. package/dist/modules/bug-reports/bug-config.d.ts +25 -0
  28. package/dist/modules/bug-reports/bug-config.js +187 -0
  29. package/dist/modules/bug-reports/bug-monitor.d.ts +108 -0
  30. package/dist/modules/bug-reports/bug-monitor.js +510 -0
  31. package/dist/modules/bug-reports/giuseppe-ai.d.ts +59 -0
  32. package/dist/modules/bug-reports/giuseppe-ai.js +335 -0
  33. package/dist/modules/bug-reports/giuseppe-bot.d.ts +109 -0
  34. package/dist/modules/bug-reports/giuseppe-bot.js +765 -0
  35. package/dist/modules/bug-reports/giuseppe-files.d.ts +52 -0
  36. package/dist/modules/bug-reports/giuseppe-files.js +338 -0
  37. package/dist/modules/bug-reports/giuseppe-git.d.ts +48 -0
  38. package/dist/modules/bug-reports/giuseppe-git.js +298 -0
  39. package/dist/modules/bug-reports/giuseppe-prompt.d.ts +5 -0
  40. package/dist/modules/bug-reports/giuseppe-prompt.js +94 -0
  41. package/dist/modules/bug-reports/index.d.ts +76 -0
  42. package/dist/modules/bug-reports/index.js +213 -0
  43. package/dist/modules/bug-reports/pending-classification-registry.d.ts +28 -0
  44. package/dist/modules/bug-reports/pending-classification-registry.js +50 -0
  45. package/dist/modules/bug-reports/pending-fix-registry.d.ts +30 -0
  46. package/dist/modules/bug-reports/pending-fix-registry.js +42 -0
  47. package/dist/modules/bug-reports/pending-registry.d.ts +27 -0
  48. package/dist/modules/bug-reports/pending-registry.js +49 -0
  49. package/dist/modules/bug-reports/types.d.ts +123 -0
  50. package/dist/modules/bug-reports/types.js +9 -0
  51. package/dist/services/bug-monitor.d.ts +23 -0
  52. package/dist/services/bug-monitor.js +275 -0
  53. package/package.json +6 -2
  54. package/.claude.tar.xz +0 -0
  55. package/lineup-manager/dist/assets/index-8ce6041d.css +0 -1
  56. package/lineup-manager/dist/assets/index-e168f265.js +0 -600
  57. package/lineup-manager/dist/index.html +0 -15
  58. package/lineup-manager/dist/manifest.json +0 -17
  59. package/lineup-manager/dist/vite.svg +0 -1
package/dist/core.js CHANGED
@@ -11,6 +11,9 @@ const config_1 = require("./config");
11
11
  const factory_1 = require("./client/factory");
12
12
  const mcp_server_1 = require("./mcp-server");
13
13
  const tool_registry_1 = require("./mcp/tool-registry");
14
+ const bug_reports_1 = require("./modules/bug-reports");
15
+ const UserContextCache_1 = require("./mcp/UserContextCache");
16
+ const bot_config_1 = require("./mcp/tools/bot-config");
14
17
  class Core {
15
18
  logger;
16
19
  appConfig;
@@ -18,6 +21,7 @@ class Core {
18
21
  mcpServer;
19
22
  daemonManager = null;
20
23
  statusLogInterval;
24
+ bugReportsModule;
21
25
  constructor() {
22
26
  // Initialize logger first
23
27
  this.logger = (0, logger_1.createLogger)({
@@ -58,10 +62,14 @@ class Core {
58
62
  await this.startMCPServer();
59
63
  }
60
64
  }
65
+ // Initialize bot config persistence (reads config from Hailer Agent Directory)
66
+ await this.initBotConfig();
61
67
  // Start client/daemon AFTER MCP server is ready
62
68
  if (this.appConfig.server.enableClient) {
63
69
  await this.startMCPClient();
64
70
  }
71
+ // Start Bug Monitor service (reads config from Hailer)
72
+ await this.startBugMonitor();
65
73
  this.setupGracefulShutdown();
66
74
  this.logger.info('All configured services started successfully');
67
75
  }
@@ -94,6 +102,66 @@ class Core {
94
102
  });
95
103
  }
96
104
  }
105
+ async startBugMonitor() {
106
+ try {
107
+ // Get first configured account for Bug Reports Module
108
+ const accounts = Object.entries(this.appConfig.hailerAccounts);
109
+ if (accounts.length === 0) {
110
+ this.logger.info('No Hailer accounts configured - Bug Reports Module disabled');
111
+ return;
112
+ }
113
+ const [apiKey] = accounts[0];
114
+ const userContext = await UserContextCache_1.UserContextCache.getContext(apiKey);
115
+ this.bugReportsModule = new bug_reports_1.BugReportsModule(userContext);
116
+ // Register bot user IDs to ignore (so bug monitor only processes human messages)
117
+ if (this.daemonManager) {
118
+ const daemonStatus = this.daemonManager.getStatus();
119
+ for (const daemon of daemonStatus) {
120
+ this.bugReportsModule.registerBotUser(daemon.botId);
121
+ this.logger.debug('Registered bot user for bug monitor to ignore', { botId: daemon.botId });
122
+ }
123
+ }
124
+ // Register giuseppe disabled handler to trigger HAL response (BEFORE start!)
125
+ this.bugReportsModule.onGiuseppeDisabled(async (bug) => {
126
+ if (this.daemonManager && bug.discussionId) {
127
+ const context = `[System notification: A new bug report "${bug.name}" was detected, but Giuseppe (the auto-fix bot) is currently disabled. The user may ask how to enable Giuseppe or want to know more about this bug. Be helpful and explain they can enable Giuseppe in the AI Hub app.]`;
128
+ await this.daemonManager.triggerHalResponse(bug.discussionId, bug.id, context);
129
+ }
130
+ });
131
+ await this.bugReportsModule.start();
132
+ if (this.bugReportsModule.isRunning()) {
133
+ const config = this.bugReportsModule.getConfig();
134
+ this.logger.info('Bug Reports Module started', {
135
+ autoFix: config.autoFix,
136
+ interval: `${config.intervalMs / 1000}s`
137
+ });
138
+ }
139
+ }
140
+ catch (error) {
141
+ this.logger.warn('Bug Reports Module failed to start - server will continue without bug monitoring', {
142
+ error: error instanceof Error ? error.message : String(error)
143
+ });
144
+ }
145
+ }
146
+ async initBotConfig() {
147
+ try {
148
+ // Get first configured account for bot config persistence
149
+ const accounts = Object.entries(this.appConfig.hailerAccounts);
150
+ if (accounts.length === 0) {
151
+ this.logger.info('No Hailer accounts configured - Bot config persistence disabled');
152
+ return;
153
+ }
154
+ const [apiKey] = accounts[0];
155
+ const userContext = await UserContextCache_1.UserContextCache.getContext(apiKey);
156
+ await (0, bot_config_1.initBotConfigPersistence)(userContext.hailer);
157
+ this.logger.info('Bot config persistence initialized');
158
+ }
159
+ catch (error) {
160
+ this.logger.warn('Bot config persistence failed to initialize - using in-memory defaults', {
161
+ error: error instanceof Error ? error.message : String(error)
162
+ });
163
+ }
164
+ }
97
165
  async initializeDaemonMode() {
98
166
  this.logger.info('Initializing Chat Agent Daemon');
99
167
  // Check for orchestrator mode via environment variable
@@ -126,6 +194,10 @@ class Core {
126
194
  if (this.statusLogInterval) {
127
195
  clearInterval(this.statusLogInterval);
128
196
  }
197
+ // Stop Bug Reports Module
198
+ if (this.bugReportsModule) {
199
+ await this.bugReportsModule.stop();
200
+ }
129
201
  if (this.daemonManager) {
130
202
  this.daemonManager.stopAll();
131
203
  this.logger.info('Chat Agent Daemon stopped');
@@ -42,4 +42,8 @@ export declare const createHailerClientByApiKey: (apiKey: string) => Promise<Hai
42
42
  * Disconnect client by API key (new unified approach)
43
43
  */
44
44
  export declare const disconnectHailerClientByApiKey: (apiKey: string) => void;
45
+ /**
46
+ * Subscribe to signals for a client connection
47
+ */
48
+ export declare const subscribeToSignal: (apiKey: string, eventType: SignalType | string, handler: SignalHandler) => (() => void) | null;
45
49
  //# sourceMappingURL=hailer-clients.d.ts.map
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.disconnectHailerClientByApiKey = exports.createHailerClientByApiKey = exports.HailerClientManager = void 0;
3
+ exports.subscribeToSignal = exports.disconnectHailerClientByApiKey = exports.createHailerClientByApiKey = exports.HailerClientManager = void 0;
4
4
  exports.getCurrentUserId = getCurrentUserId;
5
5
  const cli_1 = require("@hailer/cli");
6
6
  const auth_1 = require("./auth");
@@ -243,4 +243,19 @@ const disconnectHailerClientByApiKey = (apiKey) => {
243
243
  }
244
244
  };
245
245
  exports.disconnectHailerClientByApiKey = disconnectHailerClientByApiKey;
246
+ /**
247
+ * Subscribe to signals for a client connection
248
+ */
249
+ const subscribeToSignal = (apiKey, eventType, handler) => {
250
+ const clientManager = connectionPool.get(apiKey);
251
+ if (!clientManager) {
252
+ return null;
253
+ }
254
+ clientManager.onSignal(eventType, handler);
255
+ // Return unsubscribe function
256
+ return () => {
257
+ clientManager.offSignal(eventType, handler);
258
+ };
259
+ };
260
+ exports.subscribeToSignal = subscribeToSignal;
246
261
  //# sourceMappingURL=hailer-clients.js.map
@@ -47,6 +47,79 @@ const logger_1 = require("../../lib/logger");
47
47
  const config_1 = require("../../config");
48
48
  const tool_helpers_1 = require("../utils/tool-helpers");
49
49
  const logger = (0, logger_1.createLogger)({ component: 'app-scaffold' });
50
+ // Color palette for app icons (gradient pairs)
51
+ const APP_ICON_COLORS = [
52
+ { color1: '#3b82f6', color2: '#1d4ed8' }, // Blue
53
+ { color1: '#8b5cf6', color2: '#6d28d9' }, // Purple
54
+ { color1: '#22c55e', color2: '#15803d' }, // Green
55
+ { color1: '#f59e0b', color2: '#d97706' }, // Amber
56
+ { color1: '#ef4444', color2: '#b91c1c' }, // Red
57
+ { color1: '#14b8a6', color2: '#0d9488' }, // Teal
58
+ { color1: '#ec4899', color2: '#be185d' }, // Pink
59
+ { color1: '#6366f1', color2: '#4338ca' }, // Indigo
60
+ ];
61
+ /**
62
+ * Get initials from project name (e.g., "lineup-manager" -> "LM")
63
+ */
64
+ function getInitials(name) {
65
+ // Split by common separators
66
+ const words = name.split(/[-_\s]+/).filter(w => w.length > 0);
67
+ if (words.length >= 2) {
68
+ // Take first letter of first two words
69
+ return (words[0][0] + words[1][0]).toUpperCase();
70
+ }
71
+ else if (words.length === 1) {
72
+ // Take first two letters or first letter doubled
73
+ const word = words[0];
74
+ return word.length >= 2
75
+ ? (word[0] + word[1]).toUpperCase()
76
+ : (word[0] + word[0]).toUpperCase();
77
+ }
78
+ return 'AP'; // Default
79
+ }
80
+ /**
81
+ * Get consistent color based on name hash
82
+ */
83
+ function getColorFromName(name) {
84
+ let hash = 0;
85
+ for (let i = 0; i < name.length; i++) {
86
+ hash = ((hash << 5) - hash) + name.charCodeAt(i);
87
+ hash = hash & hash; // Convert to 32bit integer
88
+ }
89
+ const index = Math.abs(hash) % APP_ICON_COLORS.length;
90
+ return APP_ICON_COLORS[index];
91
+ }
92
+ /**
93
+ * Generate styled app icon as JPEG buffer
94
+ */
95
+ async function generateAppIcon(name) {
96
+ const sharp = (await Promise.resolve().then(() => __importStar(require('sharp')))).default;
97
+ const initials = getInitials(name);
98
+ const colors = getColorFromName(name);
99
+ const svg = `<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg">
100
+ <defs>
101
+ <linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
102
+ <stop offset="0%" style="stop-color:${colors.color1}"/>
103
+ <stop offset="100%" style="stop-color:${colors.color2}"/>
104
+ </linearGradient>
105
+ <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
106
+ <feDropShadow dx="2" dy="4" stdDeviation="3" flood-opacity="0.3"/>
107
+ </filter>
108
+ </defs>
109
+ <rect width="256" height="256" rx="50" fill="url(#bgGrad)"/>
110
+ <rect x="8" y="8" width="240" height="240" rx="42" fill="none" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
111
+ <text x="128" y="168" text-anchor="middle"
112
+ font-family="Arial Black, Arial, sans-serif"
113
+ font-size="${initials.length > 2 ? 80 : 110}"
114
+ font-weight="900"
115
+ fill="white"
116
+ filter="url(#shadow)">${initials}</text>
117
+ </svg>`;
118
+ return sharp(Buffer.from(svg))
119
+ .flatten({ background: colors.color1 })
120
+ .jpeg({ quality: 95 })
121
+ .toBuffer();
122
+ }
50
123
  const scaffoldHailerAppDescription = `Scaffold new Hailer app from template`;
51
124
  exports.scaffoldHailerAppTool = {
52
125
  name: 'scaffold_hailer_app',
@@ -93,7 +166,25 @@ exports.scaffoldHailerAppTool = {
93
166
  encoding: 'utf-8',
94
167
  shell: '/bin/bash'
95
168
  });
96
- responseText += `✅ Project scaffolded\n\n`;
169
+ responseText += `✅ Project scaffolded\n`;
170
+ // Immediately update manifest.json with correct author (before Hailer reads it)
171
+ try {
172
+ const manifestPath = path.join(projectPath, 'public', 'manifest.json');
173
+ if (fs.existsSync(manifestPath)) {
174
+ const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
175
+ const manifest = JSON.parse(manifestContent);
176
+ manifest.author = 'Hailer Oy';
177
+ if (context.email) {
178
+ manifest.contributors = [context.email];
179
+ }
180
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
181
+ responseText += ` ✅ Set author: Hailer Oy\n`;
182
+ }
183
+ }
184
+ catch {
185
+ // Non-fatal, continue
186
+ }
187
+ responseText += `\n`;
97
188
  }
98
189
  catch (error) {
99
190
  const errorMessage = (0, tool_helpers_1.extractErrorMessage)(error);
@@ -158,7 +249,7 @@ exports.scaffoldHailerAppTool = {
158
249
  }
159
250
  let appId;
160
251
  let workspaceId;
161
- // Step 4: Create dev app in Hailer
252
+ // Step 4: Create dev app in Hailer (with auto-generated icon)
162
253
  if (args.autoCreateDevApp !== false) {
163
254
  responseText += `⏳ Step 4/8: Creating dev app entry in Hailer...\n`;
164
255
  try {
@@ -167,6 +258,34 @@ exports.scaffoldHailerAppTool = {
167
258
  responseText += `⚠️ Workspace cache not available, skipping app creation\n\n`;
168
259
  }
169
260
  else {
261
+ // Generate and upload app icon
262
+ let imageId;
263
+ try {
264
+ responseText += ` 📸 Generating app icon...\n`;
265
+ const iconBuffer = await generateAppIcon(args.projectName);
266
+ // Save to temp file and upload
267
+ const os = await Promise.resolve().then(() => __importStar(require('os')));
268
+ const tempPath = path.join(os.tmpdir(), `app-icon-${Date.now()}.jpg`);
269
+ fs.writeFileSync(tempPath, iconBuffer);
270
+ const uploadResult = await context.hailer.uploadFile({
271
+ path: tempPath,
272
+ filename: 'app-icon.jpg',
273
+ isPublic: true
274
+ });
275
+ // Clean up temp file
276
+ try {
277
+ fs.unlinkSync(tempPath);
278
+ }
279
+ catch { /* ignore */ }
280
+ if (uploadResult.success && uploadResult.fileId) {
281
+ imageId = uploadResult.fileId;
282
+ responseText += ` ✅ Icon uploaded: ${imageId}\n`;
283
+ }
284
+ }
285
+ catch (iconError) {
286
+ logger.warn('Failed to generate/upload app icon', { error: iconError });
287
+ responseText += ` ⚠️ Icon generation skipped\n`;
288
+ }
170
289
  const appData = {
171
290
  cid: workspaceId,
172
291
  name: args.projectName,
@@ -175,6 +294,9 @@ exports.scaffoldHailerAppTool = {
175
294
  if (args.description) {
176
295
  appData.description = args.description;
177
296
  }
297
+ if (imageId) {
298
+ appData.image = imageId;
299
+ }
178
300
  const result = await context.hailer.request('v3.app.create', [appData]);
179
301
  appId = result.appId || result._id;
180
302
  responseText += `✅ App created: ${appId}\n`;
@@ -209,7 +331,7 @@ exports.scaffoldHailerAppTool = {
209
331
  }
210
332
  // Step 6: Update manifest.json with app ID
211
333
  if (appId) {
212
- responseText += `⏳ Step 6/8: Updating manifest.json with app ID...\n`;
334
+ responseText += `⏳ Step 6/8: Adding appId to manifest.json...\n`;
213
335
  try {
214
336
  const manifestPath = path.join(projectPath, 'public', 'manifest.json');
215
337
  if (fs.existsSync(manifestPath)) {
@@ -217,10 +339,10 @@ exports.scaffoldHailerAppTool = {
217
339
  const manifest = JSON.parse(manifestContent);
218
340
  manifest.appId = appId;
219
341
  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
220
- responseText += `✅ manifest.json updated with appId\n\n`;
342
+ responseText += ` ✅ appId: ${appId}\n\n`;
221
343
  }
222
344
  else {
223
- responseText += `⚠️ manifest.json not found at ${manifestPath}\n\n`;
345
+ responseText += `⚠️ manifest.json not found\n\n`;
224
346
  }
225
347
  }
226
348
  catch (error) {
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Bot Configuration MCP Tools
3
+ *
4
+ * Manages which bots are enabled/disabled in the workspace.
5
+ * Config is stored in the Agent Directory workflow - agents are activities.
6
+ * - Deployed agents phase = enabled
7
+ * - Retired agents phase = disabled
8
+ *
9
+ * Architecture:
10
+ * - In-memory state is primary (for speed)
11
+ * - Agent Directory workflow is persistence layer (agents as activities, phases for state)
12
+ * - On startup: Load config from Hailer by checking which phase each agent is in
13
+ * - On change: Move agent activity between phases
14
+ */
15
+ import { z } from 'zod';
16
+ import { Tool } from '../tool-registry';
17
+ import { HailerApiClient } from '../utils/index';
18
+ /**
19
+ * Available bots - single source of truth
20
+ *
21
+ * All bots that can be enabled/disabled in the workspace.
22
+ * This list should match the AI Hub app and agent definitions.
23
+ */
24
+ export declare const AVAILABLE_BOTS: {
25
+ id: string;
26
+ name: string;
27
+ description: string;
28
+ icon: string;
29
+ }[];
30
+ export declare function onBotStateChange(callback: (botId: string, enabled: boolean) => void): void;
31
+ export declare function getBotState(): Record<string, boolean>;
32
+ /**
33
+ * Set bot enabled state
34
+ *
35
+ * Updates in-memory state and persists to Agent Directory by moving
36
+ * the agent activity to the appropriate phase (deployed/retired).
37
+ */
38
+ export declare function setBotEnabled(botId: string, enabled: boolean): void;
39
+ /**
40
+ * Initialize persistence layer
41
+ * Called once during MCP server startup
42
+ *
43
+ * Loads agent enabled state from the Agent Directory workflow:
44
+ * - Agents in DEPLOYED_PHASE_ID are enabled
45
+ * - Agents in RETIRED_PHASE_ID are disabled
46
+ *
47
+ * @param client - HailerApiClient instance for API calls
48
+ */
49
+ export declare function initBotConfigPersistence(client: HailerApiClient): Promise<void>;
50
+ /**
51
+ * Check if persistence is initialized
52
+ */
53
+ export declare function isPersistenceInitialized(): boolean;
54
+ /**
55
+ * Get persistence status info (for debugging)
56
+ */
57
+ export declare function getPersistenceStatus(): {
58
+ initialized: boolean;
59
+ workflowId: string;
60
+ deployedPhaseId: string;
61
+ retiredPhaseId: string;
62
+ agentActivityIds: Record<string, string>;
63
+ hasClient: boolean;
64
+ };
65
+ /**
66
+ * List available bots and their status
67
+ */
68
+ export declare const listBotsConfigTool: Tool;
69
+ /**
70
+ * Enable a bot
71
+ */
72
+ export declare const enableBotTool: Tool;
73
+ /**
74
+ * Disable a bot
75
+ */
76
+ export declare const disableBotTool: Tool;
77
+ export declare const botConfigTools: Tool<z.ZodType<any, z.ZodTypeDef, any>>[];
78
+ //# sourceMappingURL=bot-config.d.ts.map