devchain-cli 0.6.1 → 0.7.1

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 (173) hide show
  1. package/dist/drizzle/0024_review_tables.sql +71 -0
  2. package/dist/drizzle/0025_reviews_mode_and_nullable_shas.sql +80 -0
  3. package/dist/drizzle/0026_review_comments_edited_at.sql +2 -0
  4. package/dist/drizzle/meta/_journal.json +22 -1
  5. package/dist/server/common/errors/error-types.d.ts +3 -0
  6. package/dist/server/common/errors/error-types.js +7 -1
  7. package/dist/server/common/errors/error-types.js.map +1 -1
  8. package/dist/server/common/validation/path-validation.d.ts +11 -0
  9. package/dist/server/common/validation/path-validation.js +197 -0
  10. package/dist/server/common/validation/path-validation.js.map +1 -0
  11. package/dist/server/modules/chat/dtos/chat.dto.d.ts +13 -13
  12. package/dist/server/modules/core/core.module.js +3 -2
  13. package/dist/server/modules/core/core.module.js.map +1 -1
  14. package/dist/server/modules/core/services/preflight.service.d.ts +1 -3
  15. package/dist/server/modules/core/services/preflight.service.js +2 -28
  16. package/dist/server/modules/core/services/preflight.service.js.map +1 -1
  17. package/dist/server/modules/core/services/provider-mcp-ensure.service.d.ts +26 -0
  18. package/dist/server/modules/core/services/provider-mcp-ensure.service.js +224 -0
  19. package/dist/server/modules/core/services/provider-mcp-ensure.service.js.map +1 -0
  20. package/dist/server/modules/epics/controllers/epics.controller.d.ts +3 -2
  21. package/dist/server/modules/epics/controllers/epics.controller.js +16 -6
  22. package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -1
  23. package/dist/server/modules/events/catalog/epic.created.d.ts +2 -2
  24. package/dist/server/modules/events/catalog/index.d.ts +340 -2
  25. package/dist/server/modules/events/catalog/index.js +12 -0
  26. package/dist/server/modules/events/catalog/index.js.map +1 -1
  27. package/dist/server/modules/events/catalog/review.comment.created.d.ts +69 -0
  28. package/dist/server/modules/events/catalog/review.comment.created.js +30 -0
  29. package/dist/server/modules/events/catalog/review.comment.created.js.map +1 -0
  30. package/dist/server/modules/events/catalog/review.comment.deleted.d.ts +30 -0
  31. package/dist/server/modules/events/catalog/review.comment.deleted.js +17 -0
  32. package/dist/server/modules/events/catalog/review.comment.deleted.js.map +1 -0
  33. package/dist/server/modules/events/catalog/review.comment.resolved.d.ts +30 -0
  34. package/dist/server/modules/events/catalog/review.comment.resolved.js +17 -0
  35. package/dist/server/modules/events/catalog/review.comment.resolved.js.map +1 -0
  36. package/dist/server/modules/events/catalog/review.comment.updated.d.ts +39 -0
  37. package/dist/server/modules/events/catalog/review.comment.updated.js +20 -0
  38. package/dist/server/modules/events/catalog/review.comment.updated.js.map +1 -0
  39. package/dist/server/modules/events/catalog/review.created.d.ts +90 -0
  40. package/dist/server/modules/events/catalog/review.created.js +40 -0
  41. package/dist/server/modules/events/catalog/review.created.js.map +1 -0
  42. package/dist/server/modules/events/catalog/review.updated.d.ts +110 -0
  43. package/dist/server/modules/events/catalog/review.updated.js +35 -0
  44. package/dist/server/modules/events/catalog/review.updated.js.map +1 -0
  45. package/dist/server/modules/events/subscribers/index.js +4 -0
  46. package/dist/server/modules/events/subscribers/index.js.map +1 -1
  47. package/dist/server/modules/events/subscribers/review-broadcaster.subscriber.d.ts +16 -0
  48. package/dist/server/modules/events/subscribers/review-broadcaster.subscriber.js +163 -0
  49. package/dist/server/modules/events/subscribers/review-broadcaster.subscriber.js.map +1 -0
  50. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.d.ts +23 -0
  51. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.js +217 -0
  52. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.js.map +1 -0
  53. package/dist/server/modules/git/controllers/git.controller.d.ts +24 -0
  54. package/dist/server/modules/git/controllers/git.controller.js +230 -0
  55. package/dist/server/modules/git/controllers/git.controller.js.map +1 -0
  56. package/dist/server/modules/git/dtos/git.dto.d.ts +86 -0
  57. package/dist/server/modules/git/dtos/git.dto.js +36 -0
  58. package/dist/server/modules/git/dtos/git.dto.js.map +1 -0
  59. package/dist/server/modules/git/git.module.d.ts +2 -0
  60. package/dist/server/modules/git/git.module.js +25 -0
  61. package/dist/server/modules/git/git.module.js.map +1 -0
  62. package/dist/server/modules/git/services/git.service.d.ts +71 -0
  63. package/dist/server/modules/git/services/git.service.js +550 -0
  64. package/dist/server/modules/git/services/git.service.js.map +1 -0
  65. package/dist/server/modules/mcp/controllers/mcp-http.controller.js +17 -562
  66. package/dist/server/modules/mcp/controllers/mcp-http.controller.js.map +1 -1
  67. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js +9 -568
  68. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js.map +1 -1
  69. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +226 -36
  70. package/dist/server/modules/mcp/dtos/mcp.dto.js +158 -50
  71. package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
  72. package/dist/server/modules/mcp/dtos/schema-registry.d.ts +7 -0
  73. package/dist/server/modules/mcp/dtos/schema-registry.js +56 -0
  74. package/dist/server/modules/mcp/dtos/schema-registry.js.map +1 -0
  75. package/dist/server/modules/mcp/mcp.module.js +2 -0
  76. package/dist/server/modules/mcp/mcp.module.js.map +1 -1
  77. package/dist/server/modules/mcp/services/mcp.service.d.ts +9 -1
  78. package/dist/server/modules/mcp/services/mcp.service.js +702 -33
  79. package/dist/server/modules/mcp/services/mcp.service.js.map +1 -1
  80. package/dist/server/modules/mcp/tool-definitions.d.ts +2438 -0
  81. package/dist/server/modules/mcp/tool-definitions.js +727 -0
  82. package/dist/server/modules/mcp/tool-definitions.js.map +1 -0
  83. package/dist/server/modules/mcp/utils/param-suggestion.d.ts +3 -0
  84. package/dist/server/modules/mcp/utils/param-suggestion.js +95 -0
  85. package/dist/server/modules/mcp/utils/param-suggestion.js.map +1 -0
  86. package/dist/server/modules/providers/controllers/providers.controller.d.ts +7 -6
  87. package/dist/server/modules/providers/controllers/providers.controller.js +12 -107
  88. package/dist/server/modules/providers/controllers/providers.controller.js.map +1 -1
  89. package/dist/server/modules/reviews/controllers/reviews.controller.d.ts +28 -0
  90. package/dist/server/modules/reviews/controllers/reviews.controller.js +369 -0
  91. package/dist/server/modules/reviews/controllers/reviews.controller.js.map +1 -0
  92. package/dist/server/modules/reviews/dtos/review.dto.d.ts +195 -0
  93. package/dist/server/modules/reviews/dtos/review.dto.js +92 -0
  94. package/dist/server/modules/reviews/dtos/review.dto.js.map +1 -0
  95. package/dist/server/modules/reviews/reviews.module.d.ts +2 -0
  96. package/dist/server/modules/reviews/reviews.module.js +27 -0
  97. package/dist/server/modules/reviews/reviews.module.js.map +1 -0
  98. package/dist/server/modules/reviews/services/reviews.service.d.ts +86 -0
  99. package/dist/server/modules/reviews/services/reviews.service.js +464 -0
  100. package/dist/server/modules/reviews/services/reviews.service.js.map +1 -0
  101. package/dist/server/modules/reviews/utils/notification-formatter.d.ts +12 -0
  102. package/dist/server/modules/reviews/utils/notification-formatter.js +60 -0
  103. package/dist/server/modules/reviews/utils/notification-formatter.js.map +1 -0
  104. package/dist/server/modules/sessions/services/sessions.service.d.ts +3 -1
  105. package/dist/server/modules/sessions/services/sessions.service.js +32 -2
  106. package/dist/server/modules/sessions/services/sessions.service.js.map +1 -1
  107. package/dist/server/modules/storage/db/schema.d.ts +697 -0
  108. package/dist/server/modules/storage/db/schema.js +71 -1
  109. package/dist/server/modules/storage/db/schema.js.map +1 -1
  110. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +23 -1
  111. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  112. package/dist/server/modules/storage/local/local-storage.service.d.ts +15 -2
  113. package/dist/server/modules/storage/local/local-storage.service.js +396 -7
  114. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  115. package/dist/server/modules/storage/models/domain.models.d.ts +62 -0
  116. package/dist/server/templates/claude-claude-swe.json +148 -0
  117. package/dist/server/templates/claude-codex-advanced-swe.json +139 -0
  118. package/dist/server/templates/claude-codex-advanced.json +84 -84
  119. package/dist/server/templates/claude-opus.json +102 -101
  120. package/dist/server/templates/claude-swe-single.json +105 -0
  121. package/dist/server/templates/codex-claude-swe.json +148 -0
  122. package/dist/server/templates/simple-codex.json +74 -74
  123. package/dist/server/test-setup.js +45 -0
  124. package/dist/server/test-setup.js.map +1 -1
  125. package/dist/server/tsconfig.tsbuildinfo +1 -1
  126. package/dist/server/ui/assets/ReviewDetailPage-I54h-2L-.js +6 -0
  127. package/dist/server/ui/assets/ReviewsPage-B4ua5hiX.js +19 -0
  128. package/dist/server/ui/assets/index-CqcmnFBh.css +32 -0
  129. package/dist/server/ui/assets/index-JbUMpbg7.js +858 -0
  130. package/dist/server/ui/assets/useReviewSubscription-C0GEsiRw.js +83 -0
  131. package/dist/server/ui/assets/useReviewSubscription-T3uj2-aP.css +1 -0
  132. package/dist/server/ui/index.html +2 -2
  133. package/dist/templates/claude-claude-swe.json +148 -0
  134. package/dist/templates/claude-codex-advanced-swe.json +139 -0
  135. package/dist/templates/claude-codex-advanced.json +84 -84
  136. package/dist/templates/claude-opus.json +102 -101
  137. package/dist/templates/claude-swe-single.json +105 -0
  138. package/dist/templates/codex-claude-swe.json +148 -0
  139. package/dist/templates/simple-codex.json +74 -74
  140. package/package.json +15 -2
  141. package/prebuilds/node-pty/darwin-arm64/pty.node +0 -0
  142. package/prebuilds/node-pty/darwin-arm64/spawn-helper +0 -0
  143. package/prebuilds/node-pty/darwin-x64/pty.node +0 -0
  144. package/prebuilds/node-pty/darwin-x64/spawn-helper +0 -0
  145. package/prebuilds/node-pty/linux-arm64/pty.node +0 -0
  146. package/prebuilds/node-pty/linux-x64/pty.node +0 -0
  147. package/prebuilds/node-pty/win32-arm64/conpty/OpenConsole.exe +0 -0
  148. package/prebuilds/node-pty/win32-arm64/conpty/conpty.dll +0 -0
  149. package/prebuilds/node-pty/win32-arm64/conpty.node +0 -0
  150. package/prebuilds/node-pty/win32-arm64/conpty.pdb +0 -0
  151. package/prebuilds/node-pty/win32-arm64/conpty_console_list.node +0 -0
  152. package/prebuilds/node-pty/win32-arm64/conpty_console_list.pdb +0 -0
  153. package/prebuilds/node-pty/win32-arm64/pty.node +0 -0
  154. package/prebuilds/node-pty/win32-arm64/pty.pdb +0 -0
  155. package/prebuilds/node-pty/win32-arm64/winpty-agent.exe +0 -0
  156. package/prebuilds/node-pty/win32-arm64/winpty-agent.pdb +0 -0
  157. package/prebuilds/node-pty/win32-arm64/winpty.dll +0 -0
  158. package/prebuilds/node-pty/win32-arm64/winpty.pdb +0 -0
  159. package/prebuilds/node-pty/win32-x64/conpty/OpenConsole.exe +0 -0
  160. package/prebuilds/node-pty/win32-x64/conpty/conpty.dll +0 -0
  161. package/prebuilds/node-pty/win32-x64/conpty.node +0 -0
  162. package/prebuilds/node-pty/win32-x64/conpty.pdb +0 -0
  163. package/prebuilds/node-pty/win32-x64/conpty_console_list.node +0 -0
  164. package/prebuilds/node-pty/win32-x64/conpty_console_list.pdb +0 -0
  165. package/prebuilds/node-pty/win32-x64/pty.node +0 -0
  166. package/prebuilds/node-pty/win32-x64/pty.pdb +0 -0
  167. package/prebuilds/node-pty/win32-x64/winpty-agent.exe +0 -0
  168. package/prebuilds/node-pty/win32-x64/winpty-agent.pdb +0 -0
  169. package/prebuilds/node-pty/win32-x64/winpty.dll +0 -0
  170. package/prebuilds/node-pty/win32-x64/winpty.pdb +0 -0
  171. package/scripts/postinstall.js +51 -1
  172. package/dist/server/ui/assets/index-BoDZOB7c.css +0 -32
  173. package/dist/server/ui/assets/index-hB0e02VB.js +0 -735
@@ -1,10 +1,43 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
2
18
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
19
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
20
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
21
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
22
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
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
+ })();
8
41
  var __metadata = (this && this.__metadata) || function (k, v) {
9
42
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
43
  };
@@ -23,12 +56,16 @@ const tmux_service_1 = require("../../terminal/services/tmux.service");
23
56
  const epics_service_1 = require("../../epics/services/epics.service");
24
57
  const settings_service_1 = require("../../settings/services/settings.service");
25
58
  const guests_service_1 = require("../../guests/services/guests.service");
59
+ const reviews_service_1 = require("../../reviews/services/reviews.service");
26
60
  const logger_1 = require("../../../common/logging/logger");
61
+ const schema_registry_1 = require("../dtos/schema-registry");
27
62
  const mcp_dto_1 = require("../dtos/mcp.dto");
28
63
  const instructions_resolver_1 = require("./instructions-resolver");
29
64
  const error_types_1 = require("../../../common/errors/error-types");
65
+ const path_validation_1 = require("../../../common/validation/path-validation");
30
66
  const common_2 = require("@nestjs/common");
31
67
  const zod_1 = require("zod");
68
+ const param_suggestion_1 = require("../utils/param-suggestion");
32
69
  const logger = (0, logger_1.createLogger)('McpService');
33
70
  function getActorFromContext(ctx) {
34
71
  if (ctx.type === 'agent') {
@@ -58,7 +95,7 @@ function redactParams(params) {
58
95
  return params;
59
96
  }
60
97
  let McpService = class McpService {
61
- constructor(storage, chatService, sessionsService, messagePoolService, terminalGateway, tmuxService, epicsService, settingsService, guestsService) {
98
+ constructor(storage, chatService, sessionsService, messagePoolService, terminalGateway, tmuxService, epicsService, settingsService, guestsService, reviewsService) {
62
99
  this.storage = storage;
63
100
  this.chatService = chatService;
64
101
  this.sessionsService = sessionsService;
@@ -68,6 +105,7 @@ let McpService = class McpService {
68
105
  this.epicsService = epicsService;
69
106
  this.settingsService = settingsService;
70
107
  this.guestsService = guestsService;
108
+ this.reviewsService = reviewsService;
71
109
  this.DEFAULT_INLINE_MAX_BYTES = 64 * 1024;
72
110
  logger.info('McpService initialized');
73
111
  this.featureFlags = this.storage.getFeatureFlags();
@@ -100,70 +138,84 @@ let McpService = class McpService {
100
138
  return { id: matches[0].id, name: matches[0].name };
101
139
  }
102
140
  async handleToolCall(tool, params) {
141
+ const normalizedParams = params ?? {};
142
+ const normalizedTool = tool.replace(/[.\-/]/g, '_');
103
143
  try {
104
- const normalizedTool = tool.replace(/[.\-/]/g, '_');
105
- logger.info({ tool: normalizedTool, originalTool: tool, params: redactParams(params) }, 'Handling MCP tool call');
144
+ logger.info({ tool: normalizedTool, originalTool: tool, params: redactParams(normalizedParams) }, 'Handling MCP tool call');
106
145
  switch (normalizedTool) {
107
146
  case 'devchain_create_record':
108
- return await this.createRecord(params);
147
+ return await this.createRecord(normalizedParams);
109
148
  case 'devchain_update_record':
110
- return await this.updateRecord(params);
149
+ return await this.updateRecord(normalizedParams);
111
150
  case 'devchain_get_record':
112
- return await this.getRecord(params);
151
+ return await this.getRecord(normalizedParams);
113
152
  case 'devchain_list_records':
114
- return await this.listRecords(params);
153
+ return await this.listRecords(normalizedParams);
115
154
  case 'devchain_add_tags':
116
- return await this.addTags(params);
155
+ return await this.addTags(normalizedParams);
117
156
  case 'devchain_remove_tags':
118
- return await this.removeTags(params);
157
+ return await this.removeTags(normalizedParams);
119
158
  case 'devchain_list_documents':
120
- return await this.listDocuments(params);
159
+ return await this.listDocuments(normalizedParams);
121
160
  case 'devchain_get_document':
122
- return await this.getDocument(params);
161
+ return await this.getDocument(normalizedParams);
123
162
  case 'devchain_create_document':
124
- return await this.createDocument(params);
163
+ return await this.createDocument(normalizedParams);
125
164
  case 'devchain_update_document':
126
- return await this.updateDocument(params);
165
+ return await this.updateDocument(normalizedParams);
127
166
  case 'devchain_list_prompts':
128
- return await this.listPrompts(params);
167
+ return await this.listPrompts(normalizedParams);
129
168
  case 'devchain_get_prompt':
130
- return await this.getPrompt(params);
169
+ return await this.getPrompt(normalizedParams);
131
170
  case 'devchain_list_agents':
132
- return await this.listAgents(params);
171
+ return await this.listAgents(normalizedParams);
133
172
  case 'devchain_get_agent_by_name':
134
- return await this.getAgentByName(params);
173
+ return await this.getAgentByName(normalizedParams);
135
174
  case 'devchain_list_statuses':
136
- return await this.listStatuses(params);
175
+ return await this.listStatuses(normalizedParams);
137
176
  case 'devchain_list_epics':
138
- return await this.listEpics(params);
177
+ return await this.listEpics(normalizedParams);
139
178
  case 'devchain_list_assigned_epics_tasks':
140
- return await this.listAssignedEpicsTasks(params);
179
+ return await this.listAssignedEpicsTasks(normalizedParams);
141
180
  case 'devchain_create_epic':
142
- return await this.createEpic(params);
181
+ return await this.createEpic(normalizedParams);
143
182
  case 'devchain_get_epic_by_id':
144
- return await this.getEpicById(params);
183
+ return await this.getEpicById(normalizedParams);
145
184
  case 'devchain_add_epic_comment':
146
- return await this.addEpicComment(params);
185
+ return await this.addEpicComment(normalizedParams);
147
186
  case 'devchain_update_epic':
148
- return await this.updateEpic(params);
187
+ return await this.updateEpic(normalizedParams);
149
188
  case 'notifications_initialized':
150
189
  return { success: true, data: { acknowledged: true } };
151
190
  case 'devchain_send_message':
152
- return await this.sendMessage(params);
191
+ return await this.sendMessage(normalizedParams);
153
192
  case 'devchain_chat_ack':
154
- return await this.chatAck(params);
193
+ return await this.chatAck(normalizedParams);
155
194
  case 'devchain_chat_list_members':
156
- return await this.chatListMembers(params);
195
+ return await this.chatListMembers(normalizedParams);
157
196
  case 'devchain_chat_read_history':
158
- return await this.chatReadHistory(params);
197
+ return await this.chatReadHistory(normalizedParams);
159
198
  case 'devchain_activity_start':
160
- return await this.activityStart(params);
199
+ return await this.activityStart(normalizedParams);
161
200
  case 'devchain_activity_finish':
162
- return await this.activityFinish(params);
201
+ return await this.activityFinish(normalizedParams);
163
202
  case 'devchain_list_sessions':
203
+ schema_registry_1.ListSessionsParamsSchema.parse(normalizedParams);
164
204
  return await this.listSessions();
165
205
  case 'devchain_register_guest':
166
- return await this.registerGuest(params);
206
+ return await this.registerGuest(normalizedParams);
207
+ case 'devchain_list_reviews':
208
+ return await this.listReviews(normalizedParams);
209
+ case 'devchain_get_review':
210
+ return await this.getReview(normalizedParams);
211
+ case 'devchain_get_review_comments':
212
+ return await this.getReviewComments(normalizedParams);
213
+ case 'devchain_reply_comment':
214
+ return await this.replyComment(normalizedParams);
215
+ case 'devchain_resolve_comment':
216
+ return await this.resolveComment(normalizedParams);
217
+ case 'devchain_apply_suggestion':
218
+ return await this.applySuggestion(normalizedParams);
167
219
  default:
168
220
  logger.warn({ tool: normalizedTool }, 'Unknown MCP tool');
169
221
  return {
@@ -178,12 +230,27 @@ let McpService = class McpService {
178
230
  catch (error) {
179
231
  logger.error({ tool, error }, 'MCP tool call failed');
180
232
  if (error instanceof zod_1.ZodError) {
233
+ const suggestions = [];
234
+ for (const issue of error.issues) {
235
+ if (issue.code === 'unrecognized_keys') {
236
+ const unknownKeys = issue.keys;
237
+ for (const key of unknownKeys) {
238
+ const suggestion = (0, param_suggestion_1.suggestNestedPath)(key, normalizedTool);
239
+ if (suggestion) {
240
+ suggestions.push(suggestion);
241
+ }
242
+ }
243
+ }
244
+ }
181
245
  return {
182
246
  success: false,
183
247
  error: {
184
248
  code: 'VALIDATION_ERROR',
185
249
  message: 'Invalid parameters supplied to MCP tool.',
186
- data: { issues: error.issues },
250
+ data: {
251
+ issues: error.issues,
252
+ ...(suggestions.length > 0 && { suggestions }),
253
+ },
187
254
  },
188
255
  };
189
256
  }
@@ -2637,6 +2704,606 @@ let McpService = class McpService {
2637
2704
  .replace(/[^a-z0-9]+/g, '-')
2638
2705
  .replace(/(^-|-$)/g, '');
2639
2706
  }
2707
+ async listReviews(params) {
2708
+ const validated = mcp_dto_1.ListReviewsParamsSchema.parse(params);
2709
+ const ctx = await this.resolveSessionContext(validated.sessionId);
2710
+ if (!ctx.success)
2711
+ return ctx;
2712
+ const { project } = ctx.data;
2713
+ if (!project) {
2714
+ return {
2715
+ success: false,
2716
+ error: {
2717
+ code: 'PROJECT_NOT_FOUND',
2718
+ message: 'No project associated with this session',
2719
+ },
2720
+ };
2721
+ }
2722
+ if (!this.reviewsService) {
2723
+ return {
2724
+ success: false,
2725
+ error: {
2726
+ code: 'SERVICE_UNAVAILABLE',
2727
+ message: 'ReviewsService is not available',
2728
+ },
2729
+ };
2730
+ }
2731
+ const result = await this.reviewsService.listReviews(project.id, {
2732
+ status: validated.status,
2733
+ epicId: validated.epicId,
2734
+ limit: validated.limit ?? 100,
2735
+ offset: validated.offset ?? 0,
2736
+ });
2737
+ const reviews = result.items.map((review) => ({
2738
+ id: review.id,
2739
+ title: review.title,
2740
+ description: review.description,
2741
+ status: review.status,
2742
+ baseRef: review.baseRef,
2743
+ headRef: review.headRef,
2744
+ baseSha: review.baseSha,
2745
+ headSha: review.headSha,
2746
+ epicId: review.epicId,
2747
+ createdBy: review.createdBy,
2748
+ createdByAgentId: review.createdByAgentId,
2749
+ version: review.version,
2750
+ commentCount: review.commentCount,
2751
+ createdAt: review.createdAt,
2752
+ updatedAt: review.updatedAt,
2753
+ }));
2754
+ const response = {
2755
+ reviews,
2756
+ total: result.total,
2757
+ limit: result.limit,
2758
+ offset: result.offset,
2759
+ };
2760
+ return { success: true, data: response };
2761
+ }
2762
+ async getReview(params) {
2763
+ const validated = mcp_dto_1.GetReviewParamsSchema.parse(params);
2764
+ const ctx = await this.resolveSessionContext(validated.sessionId);
2765
+ if (!ctx.success)
2766
+ return ctx;
2767
+ const { project } = ctx.data;
2768
+ if (!project) {
2769
+ return {
2770
+ success: false,
2771
+ error: {
2772
+ code: 'PROJECT_NOT_FOUND',
2773
+ message: 'No project associated with this session',
2774
+ },
2775
+ };
2776
+ }
2777
+ if (!this.reviewsService) {
2778
+ return {
2779
+ success: false,
2780
+ error: {
2781
+ code: 'SERVICE_UNAVAILABLE',
2782
+ message: 'ReviewsService is not available',
2783
+ },
2784
+ };
2785
+ }
2786
+ try {
2787
+ const reviewWithFiles = await this.reviewsService.getReview(validated.reviewId);
2788
+ if (reviewWithFiles.projectId !== project.id) {
2789
+ return {
2790
+ success: false,
2791
+ error: {
2792
+ code: 'REVIEW_NOT_FOUND',
2793
+ message: `Review ${validated.reviewId} does not belong to this project`,
2794
+ },
2795
+ };
2796
+ }
2797
+ const commentsResult = await this.reviewsService.listComments(validated.reviewId, {
2798
+ limit: 500,
2799
+ });
2800
+ const agentIds = new Set();
2801
+ for (const comment of commentsResult.items) {
2802
+ if (comment.authorAgentId)
2803
+ agentIds.add(comment.authorAgentId);
2804
+ }
2805
+ const agentNameById = new Map();
2806
+ for (const agentId of agentIds) {
2807
+ try {
2808
+ const agent = await this.storage.getAgent(agentId);
2809
+ agentNameById.set(agentId, agent.name);
2810
+ }
2811
+ catch {
2812
+ }
2813
+ }
2814
+ const comments = commentsResult.items.map((comment) => ({
2815
+ id: comment.id,
2816
+ filePath: comment.filePath,
2817
+ lineStart: comment.lineStart,
2818
+ lineEnd: comment.lineEnd,
2819
+ side: comment.side,
2820
+ content: comment.content,
2821
+ commentType: comment.commentType,
2822
+ status: comment.status,
2823
+ authorType: comment.authorType,
2824
+ authorAgentId: comment.authorAgentId,
2825
+ authorAgentName: comment.authorAgentId
2826
+ ? agentNameById.get(comment.authorAgentId)
2827
+ : undefined,
2828
+ parentId: comment.parentId,
2829
+ version: comment.version,
2830
+ createdAt: comment.createdAt,
2831
+ updatedAt: comment.updatedAt,
2832
+ }));
2833
+ const changedFiles = (reviewWithFiles.changedFiles ?? []).map((file) => ({
2834
+ path: file.path,
2835
+ status: file.status,
2836
+ additions: file.additions,
2837
+ deletions: file.deletions,
2838
+ oldPath: file.oldPath,
2839
+ }));
2840
+ const response = {
2841
+ review: {
2842
+ id: reviewWithFiles.id,
2843
+ title: reviewWithFiles.title,
2844
+ description: reviewWithFiles.description,
2845
+ status: reviewWithFiles.status,
2846
+ baseRef: reviewWithFiles.baseRef,
2847
+ headRef: reviewWithFiles.headRef,
2848
+ baseSha: reviewWithFiles.baseSha,
2849
+ headSha: reviewWithFiles.headSha,
2850
+ epicId: reviewWithFiles.epicId,
2851
+ createdBy: reviewWithFiles.createdBy,
2852
+ createdByAgentId: reviewWithFiles.createdByAgentId,
2853
+ version: reviewWithFiles.version,
2854
+ createdAt: reviewWithFiles.createdAt,
2855
+ updatedAt: reviewWithFiles.updatedAt,
2856
+ },
2857
+ changedFiles,
2858
+ comments,
2859
+ };
2860
+ return { success: true, data: response };
2861
+ }
2862
+ catch (error) {
2863
+ if (error instanceof error_types_1.NotFoundError) {
2864
+ return {
2865
+ success: false,
2866
+ error: {
2867
+ code: 'REVIEW_NOT_FOUND',
2868
+ message: `Review ${validated.reviewId} was not found`,
2869
+ },
2870
+ };
2871
+ }
2872
+ throw error;
2873
+ }
2874
+ }
2875
+ async getReviewComments(params) {
2876
+ const validated = mcp_dto_1.GetReviewCommentsParamsSchema.parse(params);
2877
+ const ctx = await this.resolveSessionContext(validated.sessionId);
2878
+ if (!ctx.success)
2879
+ return ctx;
2880
+ const { project } = ctx.data;
2881
+ if (!project) {
2882
+ return {
2883
+ success: false,
2884
+ error: {
2885
+ code: 'PROJECT_NOT_FOUND',
2886
+ message: 'No project associated with this session',
2887
+ },
2888
+ };
2889
+ }
2890
+ if (!this.reviewsService) {
2891
+ return {
2892
+ success: false,
2893
+ error: {
2894
+ code: 'SERVICE_UNAVAILABLE',
2895
+ message: 'ReviewsService is not available',
2896
+ },
2897
+ };
2898
+ }
2899
+ try {
2900
+ const review = await this.storage.getReview(validated.reviewId);
2901
+ if (review.projectId !== project.id) {
2902
+ return {
2903
+ success: false,
2904
+ error: {
2905
+ code: 'REVIEW_NOT_FOUND',
2906
+ message: `Review ${validated.reviewId} does not belong to this project`,
2907
+ },
2908
+ };
2909
+ }
2910
+ const result = await this.reviewsService.listComments(validated.reviewId, {
2911
+ status: validated.status,
2912
+ filePath: validated.filePath,
2913
+ limit: validated.limit ?? 100,
2914
+ offset: validated.offset ?? 0,
2915
+ });
2916
+ const agentIds = new Set();
2917
+ for (const comment of result.items) {
2918
+ if (comment.authorAgentId)
2919
+ agentIds.add(comment.authorAgentId);
2920
+ }
2921
+ const agentNameById = new Map();
2922
+ for (const agentId of agentIds) {
2923
+ try {
2924
+ const agent = await this.storage.getAgent(agentId);
2925
+ agentNameById.set(agentId, agent.name);
2926
+ }
2927
+ catch {
2928
+ }
2929
+ }
2930
+ const comments = result.items.map((comment) => ({
2931
+ id: comment.id,
2932
+ filePath: comment.filePath,
2933
+ lineStart: comment.lineStart,
2934
+ lineEnd: comment.lineEnd,
2935
+ side: comment.side,
2936
+ content: comment.content,
2937
+ commentType: comment.commentType,
2938
+ status: comment.status,
2939
+ authorType: comment.authorType,
2940
+ authorAgentId: comment.authorAgentId,
2941
+ authorAgentName: comment.authorAgentId
2942
+ ? agentNameById.get(comment.authorAgentId)
2943
+ : undefined,
2944
+ parentId: comment.parentId,
2945
+ version: comment.version,
2946
+ createdAt: comment.createdAt,
2947
+ updatedAt: comment.updatedAt,
2948
+ }));
2949
+ const response = {
2950
+ comments,
2951
+ total: result.total,
2952
+ limit: result.limit,
2953
+ offset: result.offset,
2954
+ };
2955
+ return { success: true, data: response };
2956
+ }
2957
+ catch (error) {
2958
+ if (error instanceof error_types_1.NotFoundError) {
2959
+ return {
2960
+ success: false,
2961
+ error: {
2962
+ code: 'REVIEW_NOT_FOUND',
2963
+ message: `Review ${validated.reviewId} was not found`,
2964
+ },
2965
+ };
2966
+ }
2967
+ throw error;
2968
+ }
2969
+ }
2970
+ async replyComment(params) {
2971
+ const validated = mcp_dto_1.ReplyCommentParamsSchema.parse(params);
2972
+ const ctx = await this.resolveSessionContext(validated.sessionId);
2973
+ if (!ctx.success)
2974
+ return ctx;
2975
+ const { project } = ctx.data;
2976
+ const actor = getActorFromContext(ctx.data);
2977
+ if (!project) {
2978
+ return {
2979
+ success: false,
2980
+ error: {
2981
+ code: 'PROJECT_NOT_FOUND',
2982
+ message: 'No project associated with this session',
2983
+ },
2984
+ };
2985
+ }
2986
+ if (!this.reviewsService) {
2987
+ return {
2988
+ success: false,
2989
+ error: {
2990
+ code: 'SERVICE_UNAVAILABLE',
2991
+ message: 'ReviewsService is not available',
2992
+ },
2993
+ };
2994
+ }
2995
+ try {
2996
+ const review = await this.storage.getReview(validated.reviewId);
2997
+ if (review.projectId !== project.id) {
2998
+ return {
2999
+ success: false,
3000
+ error: {
3001
+ code: 'REVIEW_NOT_FOUND',
3002
+ message: `Review ${validated.reviewId} does not belong to this project`,
3003
+ },
3004
+ };
3005
+ }
3006
+ const comment = await this.reviewsService.createComment(validated.reviewId, {
3007
+ parentId: validated.parentCommentId,
3008
+ content: validated.content,
3009
+ filePath: validated.filePath,
3010
+ lineStart: validated.lineStart,
3011
+ lineEnd: validated.lineEnd,
3012
+ commentType: validated.commentType ?? 'comment',
3013
+ authorType: 'agent',
3014
+ authorAgentId: actor?.id,
3015
+ targetAgentIds: validated.targetAgentIds,
3016
+ });
3017
+ const response = {
3018
+ comment: {
3019
+ id: comment.id,
3020
+ filePath: comment.filePath,
3021
+ lineStart: comment.lineStart,
3022
+ lineEnd: comment.lineEnd,
3023
+ side: comment.side,
3024
+ content: comment.content,
3025
+ commentType: comment.commentType,
3026
+ status: comment.status,
3027
+ authorType: comment.authorType,
3028
+ authorAgentId: comment.authorAgentId,
3029
+ authorAgentName: actor?.name,
3030
+ parentId: comment.parentId,
3031
+ version: comment.version,
3032
+ createdAt: comment.createdAt,
3033
+ updatedAt: comment.updatedAt,
3034
+ },
3035
+ };
3036
+ return { success: true, data: response };
3037
+ }
3038
+ catch (error) {
3039
+ if (error instanceof error_types_1.NotFoundError) {
3040
+ return {
3041
+ success: false,
3042
+ error: {
3043
+ code: 'REVIEW_NOT_FOUND',
3044
+ message: `Review ${validated.reviewId} was not found`,
3045
+ },
3046
+ };
3047
+ }
3048
+ throw error;
3049
+ }
3050
+ }
3051
+ async resolveComment(params) {
3052
+ const validated = mcp_dto_1.ResolveCommentParamsSchema.parse(params);
3053
+ const ctx = await this.resolveSessionContext(validated.sessionId);
3054
+ if (!ctx.success)
3055
+ return ctx;
3056
+ const { project } = ctx.data;
3057
+ if (!project) {
3058
+ return {
3059
+ success: false,
3060
+ error: {
3061
+ code: 'PROJECT_NOT_FOUND',
3062
+ message: 'No project associated with this session',
3063
+ },
3064
+ };
3065
+ }
3066
+ if (!this.reviewsService) {
3067
+ return {
3068
+ success: false,
3069
+ error: {
3070
+ code: 'SERVICE_UNAVAILABLE',
3071
+ message: 'ReviewsService is not available',
3072
+ },
3073
+ };
3074
+ }
3075
+ try {
3076
+ const comment = await this.storage.getReviewComment(validated.commentId);
3077
+ const review = await this.storage.getReview(comment.reviewId);
3078
+ if (review.projectId !== project.id) {
3079
+ return {
3080
+ success: false,
3081
+ error: {
3082
+ code: 'COMMENT_NOT_FOUND',
3083
+ message: `Comment ${validated.commentId} does not belong to this project`,
3084
+ },
3085
+ };
3086
+ }
3087
+ const updatedComment = await this.reviewsService.resolveComment(comment.reviewId, validated.commentId, validated.resolution, validated.version);
3088
+ let authorAgentName;
3089
+ if (updatedComment.authorAgentId) {
3090
+ try {
3091
+ const agent = await this.storage.getAgent(updatedComment.authorAgentId);
3092
+ authorAgentName = agent.name;
3093
+ }
3094
+ catch {
3095
+ }
3096
+ }
3097
+ const response = {
3098
+ comment: {
3099
+ id: updatedComment.id,
3100
+ filePath: updatedComment.filePath,
3101
+ lineStart: updatedComment.lineStart,
3102
+ lineEnd: updatedComment.lineEnd,
3103
+ side: updatedComment.side,
3104
+ content: updatedComment.content,
3105
+ commentType: updatedComment.commentType,
3106
+ status: updatedComment.status,
3107
+ authorType: updatedComment.authorType,
3108
+ authorAgentId: updatedComment.authorAgentId,
3109
+ authorAgentName,
3110
+ parentId: updatedComment.parentId,
3111
+ version: updatedComment.version,
3112
+ createdAt: updatedComment.createdAt,
3113
+ updatedAt: updatedComment.updatedAt,
3114
+ },
3115
+ };
3116
+ return { success: true, data: response };
3117
+ }
3118
+ catch (error) {
3119
+ if (error instanceof error_types_1.NotFoundError) {
3120
+ return {
3121
+ success: false,
3122
+ error: {
3123
+ code: 'COMMENT_NOT_FOUND',
3124
+ message: `Comment ${validated.commentId} was not found`,
3125
+ },
3126
+ };
3127
+ }
3128
+ throw error;
3129
+ }
3130
+ }
3131
+ async applySuggestion(params) {
3132
+ const validated = mcp_dto_1.ApplySuggestionParamsSchema.parse(params);
3133
+ const ctx = await this.resolveSessionContext(validated.sessionId);
3134
+ if (!ctx.success)
3135
+ return ctx;
3136
+ const { project } = ctx.data;
3137
+ if (!project) {
3138
+ return {
3139
+ success: false,
3140
+ error: {
3141
+ code: 'PROJECT_NOT_FOUND',
3142
+ message: 'No project associated with this session',
3143
+ },
3144
+ };
3145
+ }
3146
+ if (!this.reviewsService) {
3147
+ return {
3148
+ success: false,
3149
+ error: {
3150
+ code: 'SERVICE_UNAVAILABLE',
3151
+ message: 'ReviewsService is not available',
3152
+ },
3153
+ };
3154
+ }
3155
+ try {
3156
+ const comment = await this.storage.getReviewComment(validated.commentId);
3157
+ const review = await this.storage.getReview(comment.reviewId);
3158
+ if (review.projectId !== project.id) {
3159
+ return {
3160
+ success: false,
3161
+ error: {
3162
+ code: 'COMMENT_NOT_FOUND',
3163
+ message: `Comment ${validated.commentId} does not belong to this project`,
3164
+ },
3165
+ };
3166
+ }
3167
+ if (!comment.filePath || comment.lineStart === null) {
3168
+ return {
3169
+ success: false,
3170
+ error: {
3171
+ code: 'INVALID_SUGGESTION',
3172
+ message: 'Comment does not have file path or line information',
3173
+ },
3174
+ };
3175
+ }
3176
+ const suggestionMatch = comment.content.match(/```suggestion\s*\n([\s\S]*?)```/);
3177
+ if (!suggestionMatch) {
3178
+ return {
3179
+ success: false,
3180
+ error: {
3181
+ code: 'NO_SUGGESTION',
3182
+ message: 'Comment does not contain a suggestion block',
3183
+ },
3184
+ };
3185
+ }
3186
+ const suggestedCode = suggestionMatch[1].trimEnd();
3187
+ const lineStart = comment.lineStart;
3188
+ const lineEnd = comment.lineEnd ?? comment.lineStart;
3189
+ let validatedPath;
3190
+ try {
3191
+ validatedPath = (0, path_validation_1.validatePathWithinRoot)(project.rootPath, comment.filePath, {
3192
+ errorPrefix: 'Invalid file path in comment',
3193
+ });
3194
+ }
3195
+ catch (error) {
3196
+ if (error instanceof error_types_1.ValidationError) {
3197
+ return {
3198
+ success: false,
3199
+ error: {
3200
+ code: 'PATH_TRAVERSAL_BLOCKED',
3201
+ message: error.message,
3202
+ data: error.details,
3203
+ },
3204
+ };
3205
+ }
3206
+ throw error;
3207
+ }
3208
+ let realFilePath;
3209
+ try {
3210
+ realFilePath = await (0, path_validation_1.validateResolvedPathWithinRoot)(validatedPath.absolutePath, project.rootPath, { errorPrefix: 'Symlink validation failed' });
3211
+ }
3212
+ catch (error) {
3213
+ if (error instanceof error_types_1.ValidationError) {
3214
+ return {
3215
+ success: false,
3216
+ error: {
3217
+ code: 'SYMLINK_ESCAPE_BLOCKED',
3218
+ message: error.message,
3219
+ data: error.details,
3220
+ },
3221
+ };
3222
+ }
3223
+ throw error;
3224
+ }
3225
+ const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
3226
+ const filePath = realFilePath;
3227
+ const fileContent = await fs.readFile(filePath, 'utf-8');
3228
+ const lines = fileContent.split('\n');
3229
+ try {
3230
+ (0, path_validation_1.validateLineBounds)(lineStart, lineEnd, lines.length);
3231
+ }
3232
+ catch (error) {
3233
+ if (error instanceof error_types_1.ValidationError) {
3234
+ return {
3235
+ success: false,
3236
+ error: {
3237
+ code: 'INVALID_LINE_BOUNDS',
3238
+ message: error.message,
3239
+ data: error.details,
3240
+ },
3241
+ };
3242
+ }
3243
+ throw error;
3244
+ }
3245
+ const suggestedLines = suggestedCode.split('\n');
3246
+ lines.splice(lineStart - 1, lineEnd - lineStart + 1, ...suggestedLines);
3247
+ await fs.writeFile(filePath, lines.join('\n'), 'utf-8');
3248
+ const updatedComment = await this.reviewsService.resolveComment(comment.reviewId, validated.commentId, 'resolved', validated.version);
3249
+ let authorAgentName;
3250
+ if (updatedComment.authorAgentId) {
3251
+ try {
3252
+ const agent = await this.storage.getAgent(updatedComment.authorAgentId);
3253
+ authorAgentName = agent.name;
3254
+ }
3255
+ catch {
3256
+ }
3257
+ }
3258
+ const response = {
3259
+ comment: {
3260
+ id: updatedComment.id,
3261
+ filePath: updatedComment.filePath,
3262
+ lineStart: updatedComment.lineStart,
3263
+ lineEnd: updatedComment.lineEnd,
3264
+ side: updatedComment.side,
3265
+ content: updatedComment.content,
3266
+ commentType: updatedComment.commentType,
3267
+ status: updatedComment.status,
3268
+ authorType: updatedComment.authorType,
3269
+ authorAgentId: updatedComment.authorAgentId,
3270
+ authorAgentName,
3271
+ parentId: updatedComment.parentId,
3272
+ version: updatedComment.version,
3273
+ createdAt: updatedComment.createdAt,
3274
+ updatedAt: updatedComment.updatedAt,
3275
+ },
3276
+ applied: {
3277
+ filePath: comment.filePath,
3278
+ lineStart,
3279
+ lineEnd,
3280
+ suggestedCode,
3281
+ },
3282
+ };
3283
+ return { success: true, data: response };
3284
+ }
3285
+ catch (error) {
3286
+ if (error instanceof error_types_1.NotFoundError) {
3287
+ return {
3288
+ success: false,
3289
+ error: {
3290
+ code: 'COMMENT_NOT_FOUND',
3291
+ message: `Comment ${validated.commentId} was not found`,
3292
+ },
3293
+ };
3294
+ }
3295
+ if (error.code === 'ENOENT') {
3296
+ return {
3297
+ success: false,
3298
+ error: {
3299
+ code: 'FILE_NOT_FOUND',
3300
+ message: `File not found at path`,
3301
+ },
3302
+ };
3303
+ }
3304
+ throw error;
3305
+ }
3306
+ }
2640
3307
  };
2641
3308
  exports.McpService = McpService;
2642
3309
  exports.McpService = McpService = __decorate([
@@ -2650,6 +3317,7 @@ exports.McpService = McpService = __decorate([
2650
3317
  __param(6, (0, common_1.Inject)((0, common_1.forwardRef)(() => epics_service_1.EpicsService))),
2651
3318
  __param(7, (0, common_1.Inject)((0, common_1.forwardRef)(() => settings_service_1.SettingsService))),
2652
3319
  __param(8, (0, common_1.Inject)((0, common_1.forwardRef)(() => guests_service_1.GuestsService))),
3320
+ __param(9, (0, common_1.Inject)((0, common_1.forwardRef)(() => reviews_service_1.ReviewsService))),
2653
3321
  __metadata("design:paramtypes", [Object, chat_service_1.ChatService,
2654
3322
  sessions_service_1.SessionsService,
2655
3323
  sessions_message_pool_service_1.SessionsMessagePoolService,
@@ -2657,6 +3325,7 @@ exports.McpService = McpService = __decorate([
2657
3325
  tmux_service_1.TmuxService,
2658
3326
  epics_service_1.EpicsService,
2659
3327
  settings_service_1.SettingsService,
2660
- guests_service_1.GuestsService])
3328
+ guests_service_1.GuestsService,
3329
+ reviews_service_1.ReviewsService])
2661
3330
  ], McpService);
2662
3331
  //# sourceMappingURL=mcp.service.js.map