@hailer/mcp 0.1.17 → 0.2.2

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 (200) hide show
  1. package/dist/app.js +27 -20
  2. package/dist/core.d.ts +33 -9
  3. package/dist/core.js +279 -147
  4. package/dist/mcp/UserContextCache.js +18 -0
  5. package/dist/mcp/hailer-clients.d.ts +9 -1
  6. package/dist/mcp/hailer-clients.js +13 -3
  7. package/dist/mcp/signal-handler.js +1 -1
  8. package/dist/mcp/tool-registry.d.ts +3 -1
  9. package/dist/mcp/tool-registry.js +4 -1
  10. package/dist/mcp/tools/activity.js +43 -34
  11. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  12. package/dist/mcp/tools/bot-config/constants.js +94 -0
  13. package/dist/mcp/tools/{bot-config.d.ts → bot-config/core.d.ts} +6 -6
  14. package/dist/mcp/tools/{bot-config.js → bot-config/core.js} +15 -15
  15. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  16. package/dist/mcp/tools/bot-config/index.js +59 -0
  17. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  18. package/dist/mcp/tools/bot-config/tools.js +15 -0
  19. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  20. package/dist/mcp/tools/bot-config/types.js +6 -0
  21. package/dist/mcp/tools/bug-fixer-tools.d.ts +21 -0
  22. package/dist/mcp/tools/{giuseppe-tools.js → bug-fixer-tools.js} +61 -61
  23. package/dist/mcp/tools/user.js +10 -29
  24. package/dist/mcp/tools/workflow.js +36 -2
  25. package/dist/mcp/utils/data-transformers.d.ts +0 -8
  26. package/dist/mcp/utils/data-transformers.js +0 -28
  27. package/dist/mcp/utils/index.d.ts +4 -1
  28. package/dist/mcp/utils/index.js +17 -3
  29. package/dist/mcp/utils/pagination.d.ts +40 -0
  30. package/dist/mcp/utils/pagination.js +55 -0
  31. package/dist/mcp/utils/response-builder.d.ts +53 -0
  32. package/dist/mcp/utils/response-builder.js +110 -0
  33. package/dist/mcp/utils/tool-helpers.d.ts +0 -8
  34. package/dist/mcp/utils/tool-helpers.js +0 -24
  35. package/dist/mcp/utils/types.d.ts +1 -33
  36. package/dist/mcp-server.d.ts +2 -2
  37. package/dist/mcp-server.js +161 -139
  38. package/package.json +1 -1
  39. package/REFACTOR_STATUS.md +0 -127
  40. package/dist/agents/bot-manager.d.ts +0 -48
  41. package/dist/agents/bot-manager.js +0 -254
  42. package/dist/agents/factory.d.ts +0 -150
  43. package/dist/agents/factory.js +0 -650
  44. package/dist/agents/giuseppe/ai.d.ts +0 -83
  45. package/dist/agents/giuseppe/ai.js +0 -466
  46. package/dist/agents/giuseppe/bot.d.ts +0 -110
  47. package/dist/agents/giuseppe/bot.js +0 -780
  48. package/dist/agents/giuseppe/config.d.ts +0 -25
  49. package/dist/agents/giuseppe/config.js +0 -227
  50. package/dist/agents/giuseppe/files.d.ts +0 -52
  51. package/dist/agents/giuseppe/files.js +0 -338
  52. package/dist/agents/giuseppe/git.d.ts +0 -48
  53. package/dist/agents/giuseppe/git.js +0 -298
  54. package/dist/agents/giuseppe/index.d.ts +0 -97
  55. package/dist/agents/giuseppe/index.js +0 -258
  56. package/dist/agents/giuseppe/lsp.d.ts +0 -113
  57. package/dist/agents/giuseppe/lsp.js +0 -485
  58. package/dist/agents/giuseppe/monitor.d.ts +0 -118
  59. package/dist/agents/giuseppe/monitor.js +0 -621
  60. package/dist/agents/giuseppe/prompt.d.ts +0 -5
  61. package/dist/agents/giuseppe/prompt.js +0 -94
  62. package/dist/agents/giuseppe/registries/pending-classification.d.ts +0 -28
  63. package/dist/agents/giuseppe/registries/pending-classification.js +0 -50
  64. package/dist/agents/giuseppe/registries/pending-fix.d.ts +0 -30
  65. package/dist/agents/giuseppe/registries/pending-fix.js +0 -42
  66. package/dist/agents/giuseppe/registries/pending.d.ts +0 -27
  67. package/dist/agents/giuseppe/registries/pending.js +0 -49
  68. package/dist/agents/giuseppe/specialist.d.ts +0 -47
  69. package/dist/agents/giuseppe/specialist.js +0 -237
  70. package/dist/agents/giuseppe/types.d.ts +0 -123
  71. package/dist/agents/giuseppe/types.js +0 -9
  72. package/dist/agents/hailer-expert/index.d.ts +0 -8
  73. package/dist/agents/hailer-expert/index.js +0 -14
  74. package/dist/agents/hal/daemon.d.ts +0 -142
  75. package/dist/agents/hal/daemon.js +0 -1103
  76. package/dist/agents/hal/definitions.d.ts +0 -55
  77. package/dist/agents/hal/definitions.js +0 -263
  78. package/dist/agents/hal/index.d.ts +0 -3
  79. package/dist/agents/hal/index.js +0 -8
  80. package/dist/agents/index.d.ts +0 -18
  81. package/dist/agents/index.js +0 -48
  82. package/dist/agents/shared/base.d.ts +0 -216
  83. package/dist/agents/shared/base.js +0 -846
  84. package/dist/agents/shared/services/agent-registry.d.ts +0 -107
  85. package/dist/agents/shared/services/agent-registry.js +0 -629
  86. package/dist/agents/shared/services/conversation-manager.d.ts +0 -50
  87. package/dist/agents/shared/services/conversation-manager.js +0 -136
  88. package/dist/agents/shared/services/mcp-client.d.ts +0 -56
  89. package/dist/agents/shared/services/mcp-client.js +0 -124
  90. package/dist/agents/shared/services/message-classifier.d.ts +0 -37
  91. package/dist/agents/shared/services/message-classifier.js +0 -187
  92. package/dist/agents/shared/services/message-formatter.d.ts +0 -89
  93. package/dist/agents/shared/services/message-formatter.js +0 -371
  94. package/dist/agents/shared/services/session-logger.d.ts +0 -106
  95. package/dist/agents/shared/services/session-logger.js +0 -446
  96. package/dist/agents/shared/services/tool-executor.d.ts +0 -41
  97. package/dist/agents/shared/services/tool-executor.js +0 -169
  98. package/dist/agents/shared/services/workspace-schema-cache.d.ts +0 -125
  99. package/dist/agents/shared/services/workspace-schema-cache.js +0 -578
  100. package/dist/agents/shared/specialist.d.ts +0 -91
  101. package/dist/agents/shared/specialist.js +0 -399
  102. package/dist/agents/shared/tool-schema-loader.d.ts +0 -62
  103. package/dist/agents/shared/tool-schema-loader.js +0 -232
  104. package/dist/agents/shared/types.d.ts +0 -327
  105. package/dist/agents/shared/types.js +0 -121
  106. package/dist/client/agents/base.d.ts +0 -207
  107. package/dist/client/agents/base.js +0 -744
  108. package/dist/client/agents/definitions.d.ts +0 -53
  109. package/dist/client/agents/definitions.js +0 -263
  110. package/dist/client/agents/orchestrator.d.ts +0 -141
  111. package/dist/client/agents/orchestrator.js +0 -1062
  112. package/dist/client/agents/specialist.d.ts +0 -86
  113. package/dist/client/agents/specialist.js +0 -340
  114. package/dist/client/bot-entrypoint.d.ts +0 -7
  115. package/dist/client/bot-entrypoint.js +0 -103
  116. package/dist/client/bot-manager.d.ts +0 -44
  117. package/dist/client/bot-manager.js +0 -173
  118. package/dist/client/bot-runner.d.ts +0 -35
  119. package/dist/client/bot-runner.js +0 -188
  120. package/dist/client/chat-agent-daemon.d.ts +0 -464
  121. package/dist/client/chat-agent-daemon.js +0 -1774
  122. package/dist/client/daemon-factory.d.ts +0 -106
  123. package/dist/client/daemon-factory.js +0 -301
  124. package/dist/client/factory.d.ts +0 -111
  125. package/dist/client/factory.js +0 -314
  126. package/dist/client/index.d.ts +0 -17
  127. package/dist/client/index.js +0 -38
  128. package/dist/client/multi-bot-manager.d.ts +0 -42
  129. package/dist/client/multi-bot-manager.js +0 -161
  130. package/dist/client/orchestrator-daemon.d.ts +0 -87
  131. package/dist/client/orchestrator-daemon.js +0 -444
  132. package/dist/client/server.d.ts +0 -8
  133. package/dist/client/server.js +0 -251
  134. package/dist/client/services/agent-registry.d.ts +0 -108
  135. package/dist/client/services/agent-registry.js +0 -630
  136. package/dist/client/services/conversation-manager.d.ts +0 -50
  137. package/dist/client/services/conversation-manager.js +0 -136
  138. package/dist/client/services/mcp-client.d.ts +0 -48
  139. package/dist/client/services/mcp-client.js +0 -105
  140. package/dist/client/services/message-classifier.d.ts +0 -37
  141. package/dist/client/services/message-classifier.js +0 -187
  142. package/dist/client/services/message-formatter.d.ts +0 -84
  143. package/dist/client/services/message-formatter.js +0 -353
  144. package/dist/client/services/session-logger.d.ts +0 -106
  145. package/dist/client/services/session-logger.js +0 -446
  146. package/dist/client/services/tool-executor.d.ts +0 -41
  147. package/dist/client/services/tool-executor.js +0 -169
  148. package/dist/client/services/workspace-schema-cache.d.ts +0 -149
  149. package/dist/client/services/workspace-schema-cache.js +0 -732
  150. package/dist/client/specialist-daemon.d.ts +0 -77
  151. package/dist/client/specialist-daemon.js +0 -197
  152. package/dist/client/specialists.d.ts +0 -53
  153. package/dist/client/specialists.js +0 -178
  154. package/dist/client/tool-schema-loader.d.ts +0 -62
  155. package/dist/client/tool-schema-loader.js +0 -232
  156. package/dist/client/types.d.ts +0 -327
  157. package/dist/client/types.js +0 -121
  158. package/dist/commands/seed-config.d.ts +0 -9
  159. package/dist/commands/seed-config.js +0 -372
  160. package/dist/lib/context-manager.d.ts +0 -111
  161. package/dist/lib/context-manager.js +0 -431
  162. package/dist/lib/prompt-length-manager.d.ts +0 -81
  163. package/dist/lib/prompt-length-manager.js +0 -457
  164. package/dist/mcp/tools/giuseppe-tools.d.ts +0 -21
  165. package/dist/modules/bug-reports/bug-config.d.ts +0 -25
  166. package/dist/modules/bug-reports/bug-config.js +0 -187
  167. package/dist/modules/bug-reports/bug-monitor.d.ts +0 -108
  168. package/dist/modules/bug-reports/bug-monitor.js +0 -510
  169. package/dist/modules/bug-reports/giuseppe-agent.d.ts +0 -58
  170. package/dist/modules/bug-reports/giuseppe-agent.js +0 -467
  171. package/dist/modules/bug-reports/giuseppe-ai.d.ts +0 -83
  172. package/dist/modules/bug-reports/giuseppe-ai.js +0 -466
  173. package/dist/modules/bug-reports/giuseppe-bot.d.ts +0 -110
  174. package/dist/modules/bug-reports/giuseppe-bot.js +0 -804
  175. package/dist/modules/bug-reports/giuseppe-daemon.d.ts +0 -80
  176. package/dist/modules/bug-reports/giuseppe-daemon.js +0 -617
  177. package/dist/modules/bug-reports/giuseppe-files.d.ts +0 -64
  178. package/dist/modules/bug-reports/giuseppe-files.js +0 -375
  179. package/dist/modules/bug-reports/giuseppe-git.d.ts +0 -48
  180. package/dist/modules/bug-reports/giuseppe-git.js +0 -298
  181. package/dist/modules/bug-reports/giuseppe-lsp.d.ts +0 -113
  182. package/dist/modules/bug-reports/giuseppe-lsp.js +0 -485
  183. package/dist/modules/bug-reports/giuseppe-prompt.d.ts +0 -5
  184. package/dist/modules/bug-reports/giuseppe-prompt.js +0 -94
  185. package/dist/modules/bug-reports/index.d.ts +0 -77
  186. package/dist/modules/bug-reports/index.js +0 -215
  187. package/dist/modules/bug-reports/pending-classification-registry.d.ts +0 -28
  188. package/dist/modules/bug-reports/pending-classification-registry.js +0 -50
  189. package/dist/modules/bug-reports/pending-fix-registry.d.ts +0 -30
  190. package/dist/modules/bug-reports/pending-fix-registry.js +0 -42
  191. package/dist/modules/bug-reports/pending-registry.d.ts +0 -27
  192. package/dist/modules/bug-reports/pending-registry.js +0 -49
  193. package/dist/modules/bug-reports/types.d.ts +0 -123
  194. package/dist/modules/bug-reports/types.js +0 -9
  195. package/dist/routes/agents.d.ts +0 -44
  196. package/dist/routes/agents.js +0 -311
  197. package/dist/services/agent-credential-store.d.ts +0 -73
  198. package/dist/services/agent-credential-store.js +0 -212
  199. package/dist/services/bug-monitor.d.ts +0 -23
  200. package/dist/services/bug-monitor.js +0 -275
@@ -1,780 +0,0 @@
1
- "use strict";
2
- /**
3
- * Bug Reports Module - Giuseppe Bot
4
- *
5
- * Autonomous app fixer that:
6
- * 1. Analyzes bug reports
7
- * 2. Finds app project code
8
- * 3. Generates fixes using Claude
9
- * 4. Tests locally
10
- * 5. Publishes to production
11
- */
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.GiuseppeBot = void 0;
14
- const child_process_1 = require("child_process");
15
- const logger_1 = require("../../lib/logger");
16
- const pending_fix_1 = require("./registries/pending-fix");
17
- const pending_classification_1 = require("./registries/pending-classification");
18
- const app_scaffold_1 = require("../../mcp/tools/app-scaffold");
19
- // Import extracted modules
20
- const ai_1 = require("./ai");
21
- const git_1 = require("./git");
22
- const files_1 = require("./files");
23
- const lsp_1 = require("./lsp");
24
- 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
- // Session Log (Context Log) field IDs for logging
31
- const SESSION_LOG_CONFIG = {
32
- workflowId: '695784898d347a6c707ee397',
33
- phaseId: '695784898d347a6c707ee3c3', // Active phase
34
- teamId: '6901c67b4e80fecb6fd7b38b', // Required team
35
- fields: {
36
- contextSummary: '695784898d347a6c707ee3be',
37
- linkedWork: '695784898d347a6c707ee3c0', // Link to bug report
38
- madeBy: '695784898d347a6c707ee3c2' // Link to agent
39
- }
40
- };
41
- class GiuseppeBot {
42
- userContext;
43
- config;
44
- monitor;
45
- ai;
46
- git;
47
- files;
48
- lsp;
49
- appsRegistry = new Map();
50
- constructor(userContext, config, monitor) {
51
- this.userContext = userContext;
52
- this.config = config;
53
- this.monitor = monitor;
54
- // Initialize extracted modules
55
- const apiKey = config.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
56
- this.ai = new ai_1.GiuseppeAI(apiKey);
57
- this.git = new git_1.GiuseppeGit();
58
- this.files = new files_1.GiuseppeFiles(process.env.DEV_APPS_PATH);
59
- this.lsp = new lsp_1.GiuseppeLsp();
60
- // Load apps registry from config
61
- if (config.appsRegistry) {
62
- for (const [appId, entry] of Object.entries(config.appsRegistry)) {
63
- this.appsRegistry.set(appId, entry);
64
- }
65
- }
66
- }
67
- /**
68
- * Create a session log entry in Context Log workflow
69
- * Links to the bug report for traceability
70
- */
71
- async createSessionLogEntry(title, summary, bugActivityId) {
72
- try {
73
- const { hailer } = this.userContext;
74
- // createActivities expects array of activity objects with 'fields' (not 'fieldsAndValues')
75
- const activities = [{
76
- name: title,
77
- phaseId: SESSION_LOG_CONFIG.phaseId,
78
- teamId: SESSION_LOG_CONFIG.teamId,
79
- fields: {
80
- [SESSION_LOG_CONFIG.fields.contextSummary]: summary,
81
- [SESSION_LOG_CONFIG.fields.linkedWork]: bugActivityId
82
- }
83
- }];
84
- const result = await hailer.createActivities(SESSION_LOG_CONFIG.workflowId, activities);
85
- // Result is array of created activity IDs
86
- const createdId = result?.[0];
87
- if (createdId) {
88
- logger.info('Created session log entry', {
89
- logId: createdId,
90
- title,
91
- linkedBugId: bugActivityId
92
- });
93
- return createdId;
94
- }
95
- return null;
96
- }
97
- catch (error) {
98
- logger.warn('Failed to create session log entry', {
99
- title,
100
- bugActivityId,
101
- error: error instanceof Error ? error.message : String(error)
102
- });
103
- return null;
104
- }
105
- }
106
- /**
107
- * Check if this bug was already classified by looking for classification message in discussion
108
- */
109
- async wasAlreadyClassified(bug) {
110
- if (!bug.discussionId)
111
- return false;
112
- try {
113
- const { hailer } = this.userContext;
114
- const result = await hailer.fetchDiscussionMessages(bug.discussionId, 20);
115
- const messages = result?.messages || [];
116
- // Check if any message contains our classification pattern
117
- return messages.some((msg) => {
118
- const content = msg.content || msg.msg || msg.message || '';
119
- return content.includes('**Classification:') && content.includes('Reply **"fix it"**');
120
- });
121
- }
122
- catch (error) {
123
- logger.debug('Failed to check classification history', { bugId: bug.id, error });
124
- return false;
125
- }
126
- }
127
- /**
128
- * Handle a new bug - main entry point
129
- */
130
- async handleBug(bug) {
131
- logger.info('Giuseppe analyzing report', {
132
- bugId: bug.id,
133
- bugName: bug.name,
134
- appId: bug.appId
135
- });
136
- const result = {
137
- success: false,
138
- summary: '',
139
- log: []
140
- };
141
- // Check if already classified (e.g., after server restart)
142
- const alreadyClassified = await this.wasAlreadyClassified(bug);
143
- if (alreadyClassified) {
144
- logger.info('Bug already classified, skipping re-classification', { bugId: bug.id });
145
- // Re-register in pending registry for message watching (in case server restarted)
146
- if (bug.discussionId) {
147
- // Just register with unknown classification - we're waiting for user response
148
- pending_classification_1.pendingClassificationRegistry.register({
149
- discussionId: bug.discussionId,
150
- bugId: bug.id,
151
- bugName: bug.name,
152
- appId: bug.appId,
153
- appName: bug.appName,
154
- classification: 'unclear', // Unknown at this point
155
- reason: 'Previously classified',
156
- timestamp: Date.now(),
157
- bug
158
- });
159
- this.monitor.watchDiscussion(bug.discussionId);
160
- }
161
- return {
162
- success: false,
163
- summary: 'Already classified - awaiting confirmation'
164
- };
165
- }
166
- try {
167
- // 0. Join the bug's discussion so we can post updates
168
- await this.joinBugDiscussion(bug.id);
169
- // 1. Classify the report first
170
- const { classification, reason } = await this.ai.classifyReport(bug);
171
- result.log?.push(`Classification: ${classification} - ${reason}`);
172
- // 2. Post classification and ask for confirmation
173
- const classificationEmoji = classification === 'bug' ? '🐛' : classification === 'feature_request' ? '✨' : '❓';
174
- const classificationLabel = classification === 'bug' ? 'Bug' : classification === 'feature_request' ? 'Feature Request' : 'Unclear';
175
- const confirmMessage = [
176
- `${classificationEmoji} **Classification: ${classificationLabel}**`,
177
- '',
178
- `**Reason:** ${reason}`,
179
- '',
180
- classification === 'bug'
181
- ? `This looks like a bug I can try to fix.\n\n👉 Reply **"fix it"** to proceed with auto-fix.\n👉 Reply **"not a bug"** if this is actually a feature request.`
182
- : `This looks like a feature request, not a bug.\n\n👉 Reply **"fix it"** if you still want me to try.\n👉 Reply **"not a bug"** to confirm and close.`
183
- ].join('\n');
184
- await this.reportProgress(bug, confirmMessage);
185
- // 3. Store pending classification and wait for user response
186
- if (bug.discussionId) {
187
- pending_classification_1.pendingClassificationRegistry.register({
188
- discussionId: bug.discussionId,
189
- bugId: bug.id,
190
- bugName: bug.name,
191
- appId: bug.appId,
192
- appName: bug.appName,
193
- classification,
194
- reason,
195
- timestamp: Date.now(),
196
- bug
197
- });
198
- logger.info('Stored pending classification', {
199
- discussionId: bug.discussionId,
200
- bugId: bug.id,
201
- classification,
202
- pendingClassificationsSize: pending_classification_1.pendingClassificationRegistry.size
203
- });
204
- this.monitor.watchDiscussion(bug.discussionId);
205
- }
206
- result.summary = `Classified as ${classificationLabel} - awaiting confirmation`;
207
- result.log?.push('Waiting for user to confirm with "fix it"');
208
- return result;
209
- }
210
- catch (error) {
211
- const errorMessage = error instanceof Error ? error.message : String(error);
212
- logger.error('Classification failed', { bugId: bug.id, error: errorMessage });
213
- result.summary = `Classification error: ${errorMessage}`;
214
- return result;
215
- }
216
- }
217
- /**
218
- * Continue with bug fix after user confirms
219
- */
220
- async proceedWithFix(bug) {
221
- logger.info('Giuseppe proceeding with fix', {
222
- bugId: bug.id,
223
- bugName: bug.name,
224
- appId: bug.appId
225
- });
226
- const result = {
227
- success: false,
228
- summary: '',
229
- log: []
230
- };
231
- try {
232
- // 1. Find app project
233
- const app = await this.files.findAppProject(bug, this.appsRegistry);
234
- if (!app) {
235
- result.summary = `Could not find app project for appId: ${bug.appId}`;
236
- result.log?.push('App project not found');
237
- await this.reportProgress(bug, `❌ ${result.summary}`);
238
- return result;
239
- }
240
- result.log?.push(`Found app project: ${app.projectPath}`);
241
- await this.reportProgress(bug, `📁 Found app project: ${app.name}`);
242
- // 1.5. Run LSP analysis to understand current codebase state
243
- const preAnalysis = await this.lsp.analyzeProject(app.projectPath);
244
- if (preAnalysis.issues.length > 0) {
245
- result.log?.push(`Pre-fix LSP: ${preAnalysis.summary}`);
246
- await this.reportProgress(bug, `🔎 Codebase analysis: ${preAnalysis.summary}`);
247
- }
248
- // 2. Analyze and generate fix
249
- const allFiles = await this.files.scanSourceFiles(app.projectPath);
250
- const fixPlan = await this.ai.analyzeAndPlanFix(bug, app, allFiles, (paths) => this.files.readSelectedFiles(app.projectPath, paths));
251
- if (!fixPlan) {
252
- result.summary = 'Could not generate fix plan';
253
- result.log?.push('Fix plan generation failed');
254
- await this.reportProgress(bug, '❌ Could not analyze bug - manual review needed');
255
- return result;
256
- }
257
- result.log?.push(`Fix plan: ${fixPlan.analysis}`);
258
- await this.reportProgress(bug, `🔍 Analysis: ${fixPlan.analysis}\n🔧 Root cause: ${fixPlan.rootCause}`);
259
- // 3. Move to "In Progress"
260
- await this.monitor.moveBugToPhase(bug.id, 'inProgress');
261
- // 4. Apply fixes and build (with retry on any failure)
262
- const MAX_RETRIES = 3;
263
- let currentFixPlan = fixPlan;
264
- let fixSuccess = false;
265
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
266
- // Try to apply the fix
267
- const applyResult = await this.files.applyFixes(app, currentFixPlan.fix.files);
268
- if (!applyResult.success) {
269
- // Apply failed - likely search string not found
270
- result.log?.push(`Apply failed (attempt ${attempt}): ${applyResult.error}`);
271
- await this.reportProgress(bug, `❌ Apply failed (attempt ${attempt}/${MAX_RETRIES}): ${applyResult.error}`);
272
- if (attempt < MAX_RETRIES) {
273
- await this.reportProgress(bug, `🔄 Re-reading file and generating new fix...`);
274
- // Re-read the current state of files that need to be modified
275
- const filePaths = currentFixPlan.fix.files.map(f => f.path);
276
- const currentFiles = await this.files.readSelectedFiles(app.projectPath, filePaths);
277
- // Re-generate fix with current file state
278
- const retryFix = await this.ai.retryFixFromApplyError(bug, currentFixPlan, applyResult.error || 'Unknown error', currentFiles, attempt);
279
- if (!retryFix) {
280
- result.log?.push('Could not generate retry fix');
281
- await this.reportProgress(bug, `❌ Could not generate corrected fix`);
282
- break;
283
- }
284
- currentFixPlan = retryFix;
285
- continue; // Try applying again
286
- }
287
- break;
288
- }
289
- // Apply succeeded - now build
290
- result.filesModified = [...new Set([...(result.filesModified || []), ...applyResult.files])];
291
- result.log?.push(`Modified ${applyResult.files.length} file(s)`);
292
- await this.reportProgress(bug, `✏️ Applied fixes to ${applyResult.files.length} file(s)`);
293
- const buildResult = await this.buildAndTest(app);
294
- if (buildResult.success) {
295
- // Run LSP analysis to check for remaining issues
296
- const lspResult = await this.lsp.validateFix(app.projectPath);
297
- if (!lspResult.valid) {
298
- result.log?.push(`LSP found ${lspResult.errors.length} issue(s)`);
299
- await this.reportProgress(bug, `⚠️ Build passed but LSP found issues:\n${lspResult.errors.slice(0, 3).join('\n')}`);
300
- }
301
- fixSuccess = true;
302
- result.log?.push(`Build successful (attempt ${attempt})`);
303
- await this.reportProgress(bug, '✅ Build successful');
304
- break;
305
- }
306
- // Build failed
307
- result.log?.push(`Build failed (attempt ${attempt}): ${buildResult.error}`);
308
- await this.reportProgress(bug, `❌ Build failed (attempt ${attempt}/${MAX_RETRIES}):\n\`\`\`\n${buildResult.error}\n\`\`\``);
309
- if (attempt < MAX_RETRIES) {
310
- await this.reportProgress(bug, `🔄 Analyzing error and retrying...`);
311
- // Read the current state of files that were modified
312
- const currentFiles = await this.files.readSelectedFiles(app.projectPath, currentFixPlan.fix.files.map(f => f.path));
313
- const retryFix = await this.ai.retryFixFromError(bug, currentFixPlan, buildResult.error || 'Unknown error', currentFiles, attempt);
314
- if (!retryFix) {
315
- result.log?.push('Could not generate retry fix');
316
- await this.reportProgress(bug, `❌ Could not generate corrected fix`);
317
- break;
318
- }
319
- currentFixPlan = retryFix;
320
- // Continue to next attempt - will apply the new fix
321
- }
322
- }
323
- if (!fixSuccess) {
324
- result.summary = `Fix failed after ${MAX_RETRIES} attempts`;
325
- // Revert all changes
326
- if (result.filesModified && result.filesModified.length > 0) {
327
- await this.git.revertChanges(app, result.filesModified);
328
- }
329
- await this.reportProgress(bug, `❌ Could not fix bug after ${MAX_RETRIES} attempts. Changes reverted.`);
330
- return result;
331
- }
332
- // 6. Commit changes
333
- const commitResult = await this.git.commitChanges(app, bug);
334
- if (commitResult.hash) {
335
- result.commitHash = commitResult.hash;
336
- result.log?.push(`Committed: ${commitResult.hash}`);
337
- }
338
- // 7. Stay in "In Progress" - will move to "Fixed" only after approval
339
- // (already moved to inProgress in step 3)
340
- // 8. Store pending fix and ask owner to test
341
- if (bug.discussionId) {
342
- pending_fix_1.pendingFixRegistry.register({
343
- discussionId: bug.discussionId,
344
- bugId: bug.id,
345
- appId: bug.appId || app.name,
346
- state: 'awaiting_test',
347
- fixSummary: currentFixPlan.explanation,
348
- timestamp: Date.now(),
349
- bug,
350
- app,
351
- fixPlan: currentFixPlan,
352
- filesModified: result.filesModified || [],
353
- commitHash: result.commitHash
354
- });
355
- logger.info('Stored pending fix awaiting approval', { bugId: bug.id, discussionId: bug.discussionId });
356
- // Start watching this discussion for approval messages
357
- this.monitor.watchDiscussion(bug.discussionId);
358
- }
359
- result.success = true;
360
- result.summary = `Fixed: ${currentFixPlan.explanation} (awaiting approval)`;
361
- // Ask owner to test with clear magic words
362
- await this.reportProgress(bug, `✅ **Bug Fixed!**
363
-
364
- ${currentFixPlan.explanation}
365
-
366
- **Files changed:** ${result.filesModified?.join(', ') || 'none'}
367
-
368
- ---
369
-
370
- 🧪 **Please test the fix locally**, then reply with:
371
- - **\`approved\`** - to publish to production
372
- - **\`denied\`** - if the fix doesn't work (I'll ask what's wrong)`);
373
- return result;
374
- }
375
- catch (error) {
376
- result.summary = `Error: ${error instanceof Error ? error.message : String(error)}`;
377
- result.log?.push(`Exception: ${result.summary}`);
378
- logger.error('Giuseppe failed', { bugId: bug.id, error });
379
- await this.reportProgress(bug, `❌ Giuseppe encountered an error: ${result.summary}`);
380
- return result;
381
- }
382
- }
383
- /**
384
- * Handle a message in a bug discussion - state machine for approval flow
385
- */
386
- async handleDiscussionMessage(discussionId, message, senderId) {
387
- // Normalize: replace non-breaking spaces (0xA0) with regular spaces (0x20)
388
- // Hailer chat may send NBSP instead of regular spaces
389
- const messageLower = message.toLowerCase().replace(/\u00A0/g, ' ').trim();
390
- logger.debug('handleDiscussionMessage called', {
391
- discussionId,
392
- message: message.substring(0, 50),
393
- pendingClassificationsSize: pending_classification_1.pendingClassificationRegistry.size,
394
- hasPendingForDiscussion: pending_classification_1.pendingClassificationRegistry.has(discussionId)
395
- });
396
- // Check for pending classification first (waiting for "fix it" or "not a bug")
397
- const pendingClassification = pending_classification_1.pendingClassificationRegistry.get(discussionId);
398
- if (pendingClassification) {
399
- // Hex dump for debugging character encoding issues
400
- const hexDump = (str) => Array.from(str).map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(' ');
401
- logger.debug('Checking message against magic words', {
402
- discussionId,
403
- messageLower,
404
- messageLowerHex: hexDump(messageLower),
405
- MAGIC_WORD_NOT_A_BUG,
406
- magicWordHex: hexDump(MAGIC_WORD_NOT_A_BUG),
407
- isFixIt: messageLower === MAGIC_WORD_FIX_IT || messageLower === 'fix',
408
- isNotABug: messageLower === MAGIC_WORD_NOT_A_BUG,
409
- includesNotABug: messageLower.includes('not a bug'),
410
- includesFeature: messageLower.includes('feature')
411
- });
412
- if (messageLower === MAGIC_WORD_FIX_IT || messageLower === 'fix') {
413
- logger.debug('Matched fix it - calling handleFixItConfirmation');
414
- return await this.handleFixItConfirmation(discussionId, pendingClassification);
415
- }
416
- if (messageLower === MAGIC_WORD_NOT_A_BUG || messageLower.includes('not a bug') || messageLower.includes('feature')) {
417
- logger.debug('Matched not a bug - calling handleNotABugConfirmation');
418
- return await this.handleNotABugConfirmation(discussionId, pendingClassification);
419
- }
420
- logger.debug('No magic word match - ignoring message');
421
- // Other messages while waiting for classification - ignore
422
- return { approved: false, published: false };
423
- }
424
- // Check for pending fix (waiting for "approved" or "denied")
425
- const pendingFix = pending_fix_1.pendingFixRegistry.get(discussionId);
426
- if (!pendingFix) {
427
- return { approved: false, published: false };
428
- }
429
- // Giuseppe ONLY handles "approved" - HAL handles everything else (denial, explanations)
430
- if (messageLower === MAGIC_WORD_APPROVED) {
431
- return await this.handleApproval(discussionId, pendingFix);
432
- }
433
- // All other messages: return false, let HAL handle
434
- return { approved: false, published: false };
435
- }
436
- /**
437
- * Handle "fix it" confirmation - proceed with bug fix
438
- */
439
- async handleFixItConfirmation(discussionId, pending) {
440
- logger.info('User confirmed fix', { discussionId, bugId: pending.bugId });
441
- // Remove from pending classifications
442
- pending_classification_1.pendingClassificationRegistry.unregister(discussionId);
443
- await this.sendDiscussionMessage(discussionId, `✅ Got it! Starting bug fix...`);
444
- // Proceed with the actual fix
445
- const result = await this.proceedWithFix(pending.bug);
446
- return {
447
- approved: true,
448
- published: result.success,
449
- error: result.success ? undefined : result.summary
450
- };
451
- }
452
- /**
453
- * Handle "not a bug" confirmation - close without fixing
454
- */
455
- async handleNotABugConfirmation(discussionId, pending) {
456
- logger.info('User confirmed not a bug', { discussionId, bugId: pending.bugId });
457
- // Remove from pending classifications
458
- pending_classification_1.pendingClassificationRegistry.unregister(discussionId);
459
- this.monitor.unwatchDiscussion(discussionId);
460
- await this.sendDiscussionMessage(discussionId, `✅ Understood. This has been marked as a feature request and moved to Declined.\n\nPlease create a feature request if you'd like this change implemented.`);
461
- // Move to declined phase (not a bug)
462
- await this.monitor.moveBugToPhase(pending.bugId, 'declined');
463
- return { approved: false, published: false };
464
- }
465
- /**
466
- * Handle "approved" - publish to production
467
- */
468
- async handleApproval(discussionId, pending) {
469
- logger.info('Fix approved', { discussionId, bugId: pending.bugId });
470
- await this.sendDiscussionMessage(discussionId, `🚀 Publishing to production...`);
471
- const publishResult = await this.publishApp(pending.app, pending.bug);
472
- if (publishResult.success) {
473
- // Move to Fixed phase (published successfully)
474
- await this.monitor.moveBugToPhase(pending.bugId, 'fixed');
475
- // Remove from pending and stop watching
476
- pending_fix_1.pendingFixRegistry.unregister(discussionId);
477
- this.monitor.unwatchDiscussion(discussionId);
478
- // Log to Session Log - fix published
479
- await this.createSessionLogEntry(`Giuseppe: Published fix for ${pending.bug.name}`, [
480
- `**Bug Report:** ${pending.bug.name}`,
481
- `**App:** ${pending.app.name}`,
482
- `**Version:** ${publishResult.version || 'latest'}`,
483
- `**Files changed:** ${pending.filesModified.join(', ') || 'none'}`,
484
- '',
485
- `**Fix summary:** ${pending.fixPlan.explanation}`,
486
- '',
487
- `Fix has been tested, approved, and published to production.`
488
- ].join('\n'), pending.bugId);
489
- await this.sendDiscussionMessage(discussionId, `✅ **Published to production!** v${publishResult.version || 'latest'}\n\nBug marked as Fixed. Thank you for testing!`);
490
- logger.info('Fix published', { bugId: pending.bugId, version: publishResult.version });
491
- return { approved: true, published: true };
492
- }
493
- else {
494
- await this.sendDiscussionMessage(discussionId, `❌ Publish failed: ${publishResult.error}\n\nPlease try publishing manually or contact support.`);
495
- return { approved: true, published: false, error: publishResult.error };
496
- }
497
- }
498
- /**
499
- * Handle "denied" - ask for explanation
500
- */
501
- async handleDenial(discussionId, pending) {
502
- logger.info('Fix denied, asking for explanation', { discussionId, bugId: pending.bugId });
503
- // Change state to awaiting_explanation
504
- pending_fix_1.pendingFixRegistry.updateState(discussionId, 'awaiting_explanation');
505
- await this.sendDiscussionMessage(discussionId, `📝 **What's not working?**\n\nPlease describe what's still broken or what the fix didn't address. I'll analyze your feedback and try again.`);
506
- return { approved: false, published: false };
507
- }
508
- /**
509
- * Handle explanation after denial - retry the fix
510
- */
511
- async handleExplanationAndRetry(discussionId, pending, explanation) {
512
- logger.info('Received explanation, retrying fix', {
513
- discussionId,
514
- bugId: pending.bugId,
515
- explanation: explanation.substring(0, 100)
516
- });
517
- await this.sendDiscussionMessage(discussionId, `🔄 Got it. Analyzing your feedback and generating a new fix...`);
518
- // Revert previous changes first
519
- if (pending.filesModified.length > 0) {
520
- await this.git.revertChanges(pending.app, pending.filesModified);
521
- await this.sendDiscussionMessage(discussionId, `↩️ Reverted previous changes`);
522
- }
523
- // Get file list from git (fast, accurate)
524
- let allSourceFiles = await this.git.getSourceFilesFromGit(pending.app.projectPath);
525
- if (allSourceFiles.length === 0) {
526
- // Fallback to directory scan if git fails
527
- allSourceFiles = await this.files.scanSourceFiles(pending.app.projectPath);
528
- }
529
- // Search for relevant files based on feedback keywords
530
- const relevantFiles = await this.files.findRelevantFiles(pending.app.projectPath, explanation, pending.bug);
531
- // Always include files from previous fix
532
- const previousFixFiles = pending.fixPlan.fix.files.map(f => f.path);
533
- // Combine all relevant files (deduplicated)
534
- const filesToRead = [...new Set([
535
- ...previousFixFiles,
536
- ...relevantFiles,
537
- 'src/App.tsx' // Always include main app file
538
- ])].filter(f => allSourceFiles.includes(f));
539
- const currentFiles = await this.files.readSelectedFiles(pending.app.projectPath, filesToRead);
540
- // Generate new fix based on feedback
541
- const retryFix = await this.ai.retryFixWithFeedback(pending.bug, pending.fixPlan, explanation, allSourceFiles, currentFiles);
542
- if (!retryFix) {
543
- await this.sendDiscussionMessage(discussionId, `❌ Could not generate a new fix based on your feedback. Please provide more details or contact support.`);
544
- // Reset state to allow another try
545
- pending_fix_1.pendingFixRegistry.updateState(discussionId, 'awaiting_explanation');
546
- return { approved: false, published: false, error: 'Could not generate retry fix' };
547
- }
548
- // Apply the new fix
549
- const applyResult = await this.files.applyFixes(pending.app, retryFix.fix.files);
550
- if (!applyResult.success) {
551
- await this.sendDiscussionMessage(discussionId, `❌ Failed to apply new fix: ${applyResult.error}\n\nPlease provide more details about the issue.`);
552
- pending_fix_1.pendingFixRegistry.updateState(discussionId, 'awaiting_explanation');
553
- return { approved: false, published: false, error: applyResult.error };
554
- }
555
- // Build and test
556
- const buildResult = await this.buildAndTest(pending.app);
557
- if (!buildResult.success) {
558
- await this.sendDiscussionMessage(discussionId, `❌ Build failed:\n\`\`\`\n${buildResult.error}\n\`\`\`\n\nI'll need to try a different approach. What exactly is broken?`);
559
- // Revert and ask again
560
- await this.git.revertChanges(pending.app, applyResult.files);
561
- pending_fix_1.pendingFixRegistry.updateState(discussionId, 'awaiting_explanation');
562
- return { approved: false, published: false, error: buildResult.error };
563
- }
564
- // Success! Update pending fix and ask for approval again
565
- pending.fixPlan = retryFix;
566
- pending.filesModified = applyResult.files;
567
- pending_fix_1.pendingFixRegistry.updateState(discussionId, 'awaiting_test');
568
- await this.sendDiscussionMessage(discussionId, `✅ **New fix applied!**
569
-
570
- ${retryFix.explanation}
571
-
572
- **Files changed:** ${applyResult.files.join(', ')}
573
-
574
- ---
575
-
576
- 🧪 **Please test again**, then reply with:
577
- - **\`approved\`** - to publish to production
578
- - **\`denied\`** - if still not working`);
579
- return { approved: false, published: false, retrying: true };
580
- }
581
- /**
582
- * Check if there's a pending fix for a discussion
583
- */
584
- hasPendingFix(discussionId) {
585
- return pending_fix_1.pendingFixRegistry.has(discussionId);
586
- }
587
- /**
588
- * Retry a pending fix with explanation from HAL
589
- * Called by HAL after gathering info through conversation
590
- */
591
- async retryWithExplanation(discussionId, explanation) {
592
- const pending = pending_fix_1.pendingFixRegistry.get(discussionId);
593
- if (!pending) {
594
- logger.warn('No pending fix found for retry', { discussionId });
595
- return false;
596
- }
597
- logger.info('HAL triggered retry with explanation', {
598
- discussionId,
599
- bugId: pending.bugId,
600
- explanationLength: explanation.length
601
- });
602
- // Update registry state
603
- pending_fix_1.pendingFixRegistry.updateState(discussionId, 'awaiting_explanation');
604
- // Trigger the retry
605
- const result = await this.handleExplanationAndRetry(discussionId, pending, explanation);
606
- // Update registry state based on result
607
- if (result.retrying) {
608
- pending_fix_1.pendingFixRegistry.updateState(discussionId, 'awaiting_test');
609
- }
610
- return result.retrying || false;
611
- }
612
- /**
613
- * Initialize registry callback (call this after construction)
614
- */
615
- initializeRegistryCallback() {
616
- pending_fix_1.pendingFixRegistry.setRetryCallback(async (discussionId, explanation) => {
617
- await this.retryWithExplanation(discussionId, explanation);
618
- });
619
- logger.info('Registry retry callback initialized');
620
- }
621
- /**
622
- * Initialize classification registry callbacks for HAL coordination
623
- * This allows HAL to trigger "fix it" or "not a bug" via natural conversation
624
- */
625
- initializeClassificationCallbacks() {
626
- pending_classification_1.pendingClassificationRegistry.setCallbacks(
627
- // fixIt callback
628
- async (discussionId) => {
629
- const pending = pending_classification_1.pendingClassificationRegistry.get(discussionId);
630
- if (pending) {
631
- await this.handleFixItConfirmation(discussionId, pending);
632
- }
633
- },
634
- // notABug callback
635
- async (discussionId) => {
636
- const pending = pending_classification_1.pendingClassificationRegistry.get(discussionId);
637
- if (pending) {
638
- await this.handleNotABugConfirmation(discussionId, pending);
639
- }
640
- });
641
- logger.info('Classification callbacks initialized');
642
- }
643
- /**
644
- * Helper to send a message to a discussion
645
- */
646
- async sendDiscussionMessage(discussionId, message) {
647
- try {
648
- const { hailer } = this.userContext;
649
- await hailer.sendDiscussionMessage(discussionId, message);
650
- }
651
- catch (error) {
652
- logger.warn('Failed to send discussion message', { discussionId, error });
653
- }
654
- }
655
- /**
656
- * Build and test the app
657
- */
658
- async buildAndTest(app) {
659
- return new Promise((resolve) => {
660
- const child = (0, child_process_1.spawn)('npm', ['run', 'build'], {
661
- cwd: app.projectPath,
662
- shell: true
663
- });
664
- let stdout = '';
665
- let stderr = '';
666
- child.stdout.on('data', (data) => {
667
- stdout += data.toString();
668
- });
669
- child.stderr.on('data', (data) => {
670
- stderr += data.toString();
671
- });
672
- child.on('close', (code) => {
673
- if (code === 0) {
674
- resolve({ success: true });
675
- }
676
- else {
677
- // TypeScript errors often go to stdout, combine both
678
- const allOutput = stdout + '\n' + stderr;
679
- // Extract just the error lines (look for .tsx/.ts errors)
680
- const errorLines = allOutput.split('\n')
681
- .filter(line => line.includes('error TS') || line.includes('Error:') || line.includes('error:'))
682
- .slice(0, 10)
683
- .join('\n');
684
- resolve({ success: false, error: errorLines || allOutput.slice(-1000) });
685
- }
686
- });
687
- child.on('error', (error) => {
688
- resolve({ success: false, error: error.message });
689
- });
690
- // Timeout after 2 minutes
691
- setTimeout(() => {
692
- child.kill();
693
- resolve({ success: false, error: 'Build timeout' });
694
- }, 120000);
695
- });
696
- }
697
- /**
698
- * Join a bug's discussion using the activity ID (not discussion ID)
699
- * For activity discussions, we must use joinActivityDiscussion
700
- */
701
- async joinBugDiscussion(activityId) {
702
- try {
703
- const { hailer } = this.userContext;
704
- await hailer.joinActivityDiscussion(activityId);
705
- logger.info('Joined bug discussion', { activityId });
706
- }
707
- catch (error) {
708
- // Might already be a member, that's OK
709
- logger.debug('Could not join bug discussion (may already be member)', { activityId, error });
710
- }
711
- }
712
- /**
713
- * Publish app to production using expect script (same as MCP tool)
714
- */
715
- async publishApp(app, bug) {
716
- try {
717
- // Bump patch version before publishing (bug fix = patch)
718
- const versionBump = await this.git.bumpPatchVersion(app.projectPath);
719
- const version = versionBump?.newVersion || '1.0.0';
720
- if (versionBump) {
721
- logger.info('Version bumped for bug fix', {
722
- oldVersion: versionBump.oldVersion,
723
- newVersion: versionBump.newVersion,
724
- bugId: bug.id
725
- });
726
- // Stage and commit the version bump
727
- await this.git.commitVersionBump(app.projectPath, version);
728
- }
729
- logger.info('Publishing app via MCP tool', {
730
- projectPath: app.projectPath,
731
- version
732
- });
733
- // Use the MCP publish_hailer_app tool directly
734
- const result = await app_scaffold_1.publishHailerAppTool.execute({
735
- projectDirectory: app.projectPath,
736
- publishToMarket: false
737
- }, this.userContext);
738
- // Parse the result
739
- const text = result.content?.[0]?.type === 'text' ? result.content[0].text : '';
740
- if (text.includes('✅') || text.includes('Successfully') || text.includes('Published')) {
741
- logger.info('App published successfully', { version });
742
- // Push to git after successful publish
743
- await this.git.push(app.projectPath);
744
- // Create and push version tag (persists version history across code resets)
745
- this.git.createVersionTag(app.projectPath, version);
746
- return { success: true, version };
747
- }
748
- else {
749
- // Extract error message from response
750
- const errorMatch = text.match(/❌.*?(?:\n|$)/)?.[0] || text.substring(0, 200);
751
- logger.error('Publish failed', { error: errorMatch });
752
- return { success: false, error: errorMatch };
753
- }
754
- }
755
- catch (error) {
756
- const errorMessage = error instanceof Error ? error.message : String(error);
757
- logger.error('Publish exception', { error: errorMessage });
758
- return {
759
- success: false,
760
- error: errorMessage
761
- };
762
- }
763
- }
764
- /**
765
- * Report progress to bug discussion
766
- */
767
- async reportProgress(bug, message) {
768
- if (!bug.discussionId)
769
- return;
770
- try {
771
- const { hailer } = this.userContext;
772
- await hailer.sendDiscussionMessage(bug.discussionId, message);
773
- }
774
- catch (error) {
775
- logger.warn('Failed to report progress', { bugId: bug.id, error });
776
- }
777
- }
778
- }
779
- exports.GiuseppeBot = GiuseppeBot;
780
- //# sourceMappingURL=bot.js.map