@gitlab/gitlab-ai-provider 3.1.1 → 3.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -29,13 +29,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/index.ts
30
30
  var index_exports = {};
31
31
  __export(index_exports, {
32
- ANTHROPIC_TOOLS: () => ANTHROPIC_TOOLS,
33
- AnthropicToolExecutor: () => AnthropicToolExecutor,
34
32
  BUNDLED_CLIENT_ID: () => BUNDLED_CLIENT_ID,
35
- GITLAB_API_TOOLS: () => GITLAB_API_TOOLS,
33
+ DEFAULT_AI_GATEWAY_URL: () => DEFAULT_AI_GATEWAY_URL,
36
34
  GITLAB_COM_URL: () => GITLAB_COM_URL,
37
35
  GitLabAgenticLanguageModel: () => GitLabAgenticLanguageModel,
38
- GitLabApiToolExecutor: () => GitLabApiToolExecutor,
36
+ GitLabDirectAccessClient: () => GitLabDirectAccessClient,
39
37
  GitLabError: () => GitLabError,
40
38
  GitLabOAuthManager: () => GitLabOAuthManager,
41
39
  GitLabProjectCache: () => GitLabProjectCache,
@@ -45,8 +43,7 @@ __export(index_exports, {
45
43
  TOKEN_EXPIRY_SKEW_MS: () => TOKEN_EXPIRY_SKEW_MS,
46
44
  createGitLab: () => createGitLab,
47
45
  getAnthropicModelForModelId: () => getAnthropicModelForModelId,
48
- gitlab: () => gitlab,
49
- isGitLabApiTool: () => isGitLabApiTool
46
+ gitlab: () => gitlab
50
47
  });
51
48
  module.exports = __toCommonJS(index_exports);
52
49
 
@@ -97,14 +94,17 @@ var directAccessTokenSchema = import_zod.z.object({
97
94
  headers: import_zod.z.record(import_zod.z.string()),
98
95
  token: import_zod.z.string()
99
96
  });
97
+ var DEFAULT_AI_GATEWAY_URL = "https://cloud.gitlab.com";
100
98
  var GitLabDirectAccessClient = class {
101
99
  config;
102
100
  fetchFn;
101
+ aiGatewayUrl;
103
102
  cachedToken = null;
104
103
  tokenExpiresAt = 0;
105
104
  constructor(config) {
106
105
  this.config = config;
107
106
  this.fetchFn = config.fetch ?? fetch;
107
+ this.aiGatewayUrl = config.aiGatewayUrl || process.env["GITLAB_AI_GATEWAY_URL"] || DEFAULT_AI_GATEWAY_URL;
108
108
  }
109
109
  /**
110
110
  * Get a direct access token for the Anthropic proxy.
@@ -141,12 +141,23 @@ var GitLabDirectAccessClient = class {
141
141
  return await this.getDirectAccessToken(true);
142
142
  } catch (refreshError) {
143
143
  throw new GitLabError({
144
- message: `Failed to get direct access token: ${response.status} ${response.statusText} - ${errorText}`
144
+ message: `Failed to get direct access token: ${response.status} ${response.statusText} - ${errorText}`,
145
+ statusCode: response.status,
146
+ responseBody: errorText
145
147
  });
146
148
  }
147
149
  }
150
+ if (response.status === 403) {
151
+ throw new GitLabError({
152
+ message: `Access denied to GitLab AI features (${this.config.instanceUrl}). This may indicate that: (1) GitLab Duo is not enabled on this instance, (2) Your account does not have access to AI features, or (3) The third-party agents feature is not available. Original error: ${response.status} ${response.statusText} - ${errorText}`,
153
+ statusCode: response.status,
154
+ responseBody: errorText
155
+ });
156
+ }
148
157
  throw new GitLabError({
149
- message: `Failed to get direct access token: ${response.status} ${response.statusText} - ${errorText}`
158
+ message: `Failed to get direct access token: ${response.status} ${response.statusText} - ${errorText}`,
159
+ statusCode: response.status,
160
+ responseBody: errorText
150
161
  });
151
162
  }
152
163
  const data = await response.json();
@@ -168,7 +179,8 @@ var GitLabDirectAccessClient = class {
168
179
  * Get the Anthropic proxy base URL
169
180
  */
170
181
  getAnthropicProxyUrl() {
171
- return "https://cloud.gitlab.com/ai/v1/proxy/anthropic/";
182
+ const baseUrl = this.aiGatewayUrl.replace(/\/$/, "");
183
+ return `${baseUrl}/ai/v1/proxy/anthropic/`;
172
184
  }
173
185
  /**
174
186
  * Invalidate the cached token
@@ -195,7 +207,8 @@ var GitLabAgenticLanguageModel = class {
195
207
  getHeaders: config.getHeaders,
196
208
  refreshApiKey: config.refreshApiKey,
197
209
  fetch: config.fetch,
198
- featureFlags: config.featureFlags
210
+ featureFlags: config.featureFlags,
211
+ aiGatewayUrl: config.aiGatewayUrl
199
212
  });
200
213
  }
201
214
  get provider() {
@@ -207,10 +220,12 @@ var GitLabAgenticLanguageModel = class {
207
220
  */
208
221
  async getAnthropicClient(forceRefresh = false) {
209
222
  const tokenData = await this.directAccessClient.getDirectAccessToken(forceRefresh);
223
+ const { "x-api-key": _removed, ...filteredHeaders } = tokenData.headers;
210
224
  this.anthropicClient = new import_sdk.default({
225
+ apiKey: null,
211
226
  authToken: tokenData.token,
212
227
  baseURL: this.directAccessClient.getAnthropicProxyUrl(),
213
- defaultHeaders: tokenData.headers
228
+ defaultHeaders: filteredHeaders
214
229
  });
215
230
  return this.anthropicClient;
216
231
  }
@@ -444,114 +459,155 @@ var GitLabAgenticLanguageModel = class {
444
459
  const self = this;
445
460
  const stream = new ReadableStream({
446
461
  start: async (controller) => {
462
+ const contentBlocks = {};
463
+ const usage = {
464
+ inputTokens: 0,
465
+ outputTokens: 0,
466
+ totalTokens: 0
467
+ };
468
+ let finishReason = "unknown";
447
469
  try {
448
- const anthropicStream = client.messages.stream(requestBody);
449
- let currentTextBlockId = null;
450
- let currentToolBlockId = null;
451
- let currentToolName = null;
452
- const usage = {
453
- inputTokens: 0,
454
- outputTokens: 0,
455
- totalTokens: 0
456
- };
457
- let finishReason = "unknown";
470
+ const anthropicStream = client.messages.stream(requestBody, {
471
+ signal: options.abortSignal
472
+ });
458
473
  controller.enqueue({
459
474
  type: "stream-start",
460
475
  warnings: []
461
476
  });
462
- for await (const event of anthropicStream) {
463
- switch (event.type) {
464
- case "message_start":
465
- if (event.message.usage) {
466
- usage.inputTokens = event.message.usage.input_tokens;
467
- }
468
- controller.enqueue({
469
- type: "response-metadata",
470
- id: event.message.id,
471
- modelId: event.message.model
472
- });
473
- break;
474
- case "content_block_start":
475
- if (event.content_block.type === "text") {
476
- currentTextBlockId = `text-${event.index}`;
477
- controller.enqueue({
478
- type: "text-start",
479
- id: currentTextBlockId
480
- });
481
- } else if (event.content_block.type === "tool_use") {
482
- currentToolBlockId = event.content_block.id;
483
- currentToolName = event.content_block.name;
484
- controller.enqueue({
485
- type: "tool-input-start",
486
- id: currentToolBlockId,
487
- toolName: currentToolName
488
- });
489
- }
490
- break;
491
- case "content_block_delta":
492
- if (event.delta.type === "text_delta" && currentTextBlockId) {
493
- controller.enqueue({
494
- type: "text-delta",
495
- id: currentTextBlockId,
496
- delta: event.delta.text
497
- });
498
- } else if (event.delta.type === "input_json_delta" && currentToolBlockId) {
499
- controller.enqueue({
500
- type: "tool-input-delta",
501
- id: currentToolBlockId,
502
- delta: event.delta.partial_json
503
- });
504
- }
505
- break;
506
- case "content_block_stop":
507
- if (currentTextBlockId) {
508
- controller.enqueue({
509
- type: "text-end",
510
- id: currentTextBlockId
511
- });
512
- currentTextBlockId = null;
513
- }
514
- if (currentToolBlockId) {
515
- controller.enqueue({
516
- type: "tool-input-end",
517
- id: currentToolBlockId
518
- });
519
- currentToolBlockId = null;
520
- currentToolName = null;
521
- }
522
- break;
523
- case "message_delta":
524
- if (event.usage) {
525
- usage.outputTokens = event.usage.output_tokens;
526
- usage.totalTokens = (usage.inputTokens || 0) + event.usage.output_tokens;
527
- }
528
- if (event.delta.stop_reason) {
529
- finishReason = self.convertFinishReason(event.delta.stop_reason);
530
- }
531
- break;
532
- case "message_stop": {
533
- const finalMessage = await anthropicStream.finalMessage();
534
- for (const block of finalMessage.content) {
535
- if (block.type === "tool_use") {
477
+ await new Promise((resolve2, reject) => {
478
+ anthropicStream.on("streamEvent", (event) => {
479
+ try {
480
+ switch (event.type) {
481
+ case "message_start":
482
+ if (event.message.usage) {
483
+ usage.inputTokens = event.message.usage.input_tokens;
484
+ }
485
+ controller.enqueue({
486
+ type: "response-metadata",
487
+ id: event.message.id,
488
+ modelId: event.message.model
489
+ });
490
+ break;
491
+ case "content_block_start":
492
+ if (event.content_block.type === "text") {
493
+ const textId = `text-${event.index}`;
494
+ contentBlocks[event.index] = { type: "text", id: textId };
495
+ controller.enqueue({
496
+ type: "text-start",
497
+ id: textId
498
+ });
499
+ } else if (event.content_block.type === "tool_use") {
500
+ contentBlocks[event.index] = {
501
+ type: "tool-call",
502
+ toolCallId: event.content_block.id,
503
+ toolName: event.content_block.name,
504
+ input: ""
505
+ };
506
+ controller.enqueue({
507
+ type: "tool-input-start",
508
+ id: event.content_block.id,
509
+ toolName: event.content_block.name
510
+ });
511
+ }
512
+ break;
513
+ case "content_block_delta": {
514
+ const block = contentBlocks[event.index];
515
+ if (event.delta.type === "text_delta" && block?.type === "text") {
516
+ controller.enqueue({
517
+ type: "text-delta",
518
+ id: block.id,
519
+ delta: event.delta.text
520
+ });
521
+ } else if (event.delta.type === "input_json_delta" && block?.type === "tool-call") {
522
+ block.input += event.delta.partial_json;
523
+ controller.enqueue({
524
+ type: "tool-input-delta",
525
+ id: block.toolCallId,
526
+ delta: event.delta.partial_json
527
+ });
528
+ }
529
+ break;
530
+ }
531
+ case "content_block_stop": {
532
+ const block = contentBlocks[event.index];
533
+ if (block?.type === "text") {
534
+ controller.enqueue({
535
+ type: "text-end",
536
+ id: block.id
537
+ });
538
+ } else if (block?.type === "tool-call") {
539
+ controller.enqueue({
540
+ type: "tool-input-end",
541
+ id: block.toolCallId
542
+ });
543
+ controller.enqueue({
544
+ type: "tool-call",
545
+ toolCallId: block.toolCallId,
546
+ toolName: block.toolName,
547
+ input: block.input === "" ? "{}" : block.input
548
+ });
549
+ }
550
+ delete contentBlocks[event.index];
551
+ break;
552
+ }
553
+ case "message_delta":
554
+ if (event.usage) {
555
+ usage.outputTokens = event.usage.output_tokens;
556
+ usage.totalTokens = (usage.inputTokens || 0) + event.usage.output_tokens;
557
+ }
558
+ if (event.delta.stop_reason) {
559
+ finishReason = self.convertFinishReason(event.delta.stop_reason);
560
+ }
561
+ break;
562
+ case "message_stop": {
536
563
  controller.enqueue({
537
- type: "tool-call",
538
- toolCallId: block.id,
539
- toolName: block.name,
540
- input: JSON.stringify(block.input)
564
+ type: "finish",
565
+ finishReason,
566
+ usage
541
567
  });
568
+ break;
542
569
  }
543
570
  }
544
- controller.enqueue({
545
- type: "finish",
546
- finishReason,
547
- usage
548
- });
549
- break;
571
+ } catch {
550
572
  }
573
+ });
574
+ anthropicStream.on("end", () => {
575
+ resolve2();
576
+ });
577
+ anthropicStream.on("error", (error) => {
578
+ reject(error);
579
+ });
580
+ });
581
+ for (const [, block] of Object.entries(contentBlocks)) {
582
+ if (block.type === "tool-call") {
583
+ controller.enqueue({
584
+ type: "tool-input-end",
585
+ id: block.toolCallId
586
+ });
587
+ controller.enqueue({
588
+ type: "tool-call",
589
+ toolCallId: block.toolCallId,
590
+ toolName: block.toolName,
591
+ input: block.input === "" ? "{}" : block.input
592
+ });
551
593
  }
552
594
  }
553
595
  controller.close();
554
596
  } catch (error) {
597
+ for (const [, block] of Object.entries(contentBlocks)) {
598
+ if (block.type === "tool-call") {
599
+ controller.enqueue({
600
+ type: "tool-input-end",
601
+ id: block.toolCallId
602
+ });
603
+ controller.enqueue({
604
+ type: "tool-call",
605
+ toolCallId: block.toolCallId,
606
+ toolName: block.toolName,
607
+ input: block.input === "" ? "{}" : block.input
608
+ });
609
+ }
610
+ }
555
611
  if (!isRetry && self.isTokenError(error)) {
556
612
  self.directAccessClient.invalidateToken();
557
613
  controller.enqueue({
@@ -909,7 +965,8 @@ function createGitLab(options = {}) {
909
965
  fetch: options.fetch,
910
966
  anthropicModel: agenticOptions?.anthropicModel ?? getAnthropicModelForModelId(modelId),
911
967
  maxTokens: agenticOptions?.maxTokens,
912
- featureFlags
968
+ featureFlags,
969
+ aiGatewayUrl: options.aiGatewayUrl
913
970
  });
914
971
  };
915
972
  const createDefaultModel = (modelId) => {
@@ -935,1196 +992,9 @@ function createGitLab(options = {}) {
935
992
  }
936
993
  var gitlab = createGitLab();
937
994
 
938
- // src/gitlab-api-tools.ts
939
- var GITLAB_API_TOOLS = [
940
- // Merge Request Tools
941
- {
942
- name: "gitlab_get_merge_request",
943
- description: `Get details of a specific merge request by project and MR IID.
944
- Returns: title, description, state, author, assignees, reviewers, labels, diff stats, and discussion notes.`,
945
- input_schema: {
946
- type: "object",
947
- properties: {
948
- project_id: {
949
- type: "string",
950
- description: 'The project ID or URL-encoded path (e.g., "gitlab-org/gitlab" or "123")'
951
- },
952
- mr_iid: {
953
- type: "number",
954
- description: "The internal ID of the merge request within the project"
955
- },
956
- include_changes: {
957
- type: "boolean",
958
- description: "Whether to include the list of changed files (default: false)"
959
- }
960
- },
961
- required: ["project_id", "mr_iid"]
962
- }
963
- },
964
- {
965
- name: "gitlab_list_merge_requests",
966
- description: `List merge requests for a project or search globally.
967
- Can filter by state (opened, closed, merged, all), scope (assigned_to_me, created_by_me), and labels.`,
968
- input_schema: {
969
- type: "object",
970
- properties: {
971
- project_id: {
972
- type: "string",
973
- description: "The project ID or path. If not provided, searches globally."
974
- },
975
- state: {
976
- type: "string",
977
- enum: ["opened", "closed", "merged", "all"],
978
- description: "Filter by MR state (default: opened)"
979
- },
980
- scope: {
981
- type: "string",
982
- enum: ["assigned_to_me", "created_by_me", "all"],
983
- description: "Filter by scope"
984
- },
985
- search: {
986
- type: "string",
987
- description: "Search MRs by title or description"
988
- },
989
- labels: {
990
- type: "string",
991
- description: "Comma-separated list of labels to filter by"
992
- },
993
- limit: {
994
- type: "number",
995
- description: "Maximum number of results (default: 20)"
996
- }
997
- },
998
- required: []
999
- }
1000
- },
1001
- {
1002
- name: "gitlab_get_mr_changes",
1003
- description: `Get the file changes (diff) for a merge request.
1004
- Returns the list of files changed with their diffs.`,
1005
- input_schema: {
1006
- type: "object",
1007
- properties: {
1008
- project_id: {
1009
- type: "string",
1010
- description: "The project ID or URL-encoded path"
1011
- },
1012
- mr_iid: {
1013
- type: "number",
1014
- description: "The internal ID of the merge request"
1015
- }
1016
- },
1017
- required: ["project_id", "mr_iid"]
1018
- }
1019
- },
1020
- {
1021
- name: "gitlab_list_mr_discussions",
1022
- description: `List discussions (comments/threads) on a merge request.
1023
- Returns all discussion threads including resolved status.`,
1024
- input_schema: {
1025
- type: "object",
1026
- properties: {
1027
- project_id: {
1028
- type: "string",
1029
- description: "The project ID or URL-encoded path"
1030
- },
1031
- mr_iid: {
1032
- type: "number",
1033
- description: "The internal ID of the merge request"
1034
- }
1035
- },
1036
- required: ["project_id", "mr_iid"]
1037
- }
1038
- },
1039
- {
1040
- name: "gitlab_create_mr_note",
1041
- description: `Add a comment/note to a merge request.`,
1042
- input_schema: {
1043
- type: "object",
1044
- properties: {
1045
- project_id: {
1046
- type: "string",
1047
- description: "The project ID or URL-encoded path"
1048
- },
1049
- mr_iid: {
1050
- type: "number",
1051
- description: "The internal ID of the merge request"
1052
- },
1053
- body: {
1054
- type: "string",
1055
- description: "The content of the note/comment (supports Markdown)"
1056
- }
1057
- },
1058
- required: ["project_id", "mr_iid", "body"]
1059
- }
1060
- },
1061
- // Issue Tools
1062
- {
1063
- name: "gitlab_get_issue",
1064
- description: `Get details of a specific issue by project and issue IID.
1065
- Returns: title, description, state, author, assignees, labels, milestone, weight, and comments.`,
1066
- input_schema: {
1067
- type: "object",
1068
- properties: {
1069
- project_id: {
1070
- type: "string",
1071
- description: "The project ID or URL-encoded path"
1072
- },
1073
- issue_iid: {
1074
- type: "number",
1075
- description: "The internal ID of the issue within the project"
1076
- }
1077
- },
1078
- required: ["project_id", "issue_iid"]
1079
- }
1080
- },
1081
- {
1082
- name: "gitlab_list_issues",
1083
- description: `List issues for a project or search globally.
1084
- Can filter by state, labels, assignee, milestone.`,
1085
- input_schema: {
1086
- type: "object",
1087
- properties: {
1088
- project_id: {
1089
- type: "string",
1090
- description: "The project ID or path. If not provided, searches globally."
1091
- },
1092
- state: {
1093
- type: "string",
1094
- enum: ["opened", "closed", "all"],
1095
- description: "Filter by issue state (default: opened)"
1096
- },
1097
- scope: {
1098
- type: "string",
1099
- enum: ["assigned_to_me", "created_by_me", "all"],
1100
- description: "Filter by scope"
1101
- },
1102
- search: {
1103
- type: "string",
1104
- description: "Search issues by title or description"
1105
- },
1106
- labels: {
1107
- type: "string",
1108
- description: "Comma-separated list of labels to filter by"
1109
- },
1110
- milestone: {
1111
- type: "string",
1112
- description: "Filter by milestone title"
1113
- },
1114
- limit: {
1115
- type: "number",
1116
- description: "Maximum number of results (default: 20)"
1117
- }
1118
- },
1119
- required: []
1120
- }
1121
- },
1122
- {
1123
- name: "gitlab_create_issue_note",
1124
- description: `Add a comment/note to an issue.`,
1125
- input_schema: {
1126
- type: "object",
1127
- properties: {
1128
- project_id: {
1129
- type: "string",
1130
- description: "The project ID or URL-encoded path"
1131
- },
1132
- issue_iid: {
1133
- type: "number",
1134
- description: "The internal ID of the issue"
1135
- },
1136
- body: {
1137
- type: "string",
1138
- description: "The content of the note/comment (supports Markdown)"
1139
- }
1140
- },
1141
- required: ["project_id", "issue_iid", "body"]
1142
- }
1143
- },
1144
- // Pipeline/CI Tools
1145
- {
1146
- name: "gitlab_list_pipelines",
1147
- description: `List pipelines for a project.
1148
- Can filter by status, ref (branch/tag), username.`,
1149
- input_schema: {
1150
- type: "object",
1151
- properties: {
1152
- project_id: {
1153
- type: "string",
1154
- description: "The project ID or URL-encoded path"
1155
- },
1156
- status: {
1157
- type: "string",
1158
- enum: ["running", "pending", "success", "failed", "canceled", "skipped", "manual"],
1159
- description: "Filter by pipeline status"
1160
- },
1161
- ref: {
1162
- type: "string",
1163
- description: "Filter by branch or tag name"
1164
- },
1165
- limit: {
1166
- type: "number",
1167
- description: "Maximum number of results (default: 20)"
1168
- }
1169
- },
1170
- required: ["project_id"]
1171
- }
1172
- },
1173
- {
1174
- name: "gitlab_get_pipeline",
1175
- description: `Get details of a specific pipeline including its jobs.`,
1176
- input_schema: {
1177
- type: "object",
1178
- properties: {
1179
- project_id: {
1180
- type: "string",
1181
- description: "The project ID or URL-encoded path"
1182
- },
1183
- pipeline_id: {
1184
- type: "number",
1185
- description: "The ID of the pipeline"
1186
- }
1187
- },
1188
- required: ["project_id", "pipeline_id"]
1189
- }
1190
- },
1191
- {
1192
- name: "gitlab_list_pipeline_jobs",
1193
- description: `List jobs for a pipeline, optionally filter by scope (failed, success, etc).`,
1194
- input_schema: {
1195
- type: "object",
1196
- properties: {
1197
- project_id: {
1198
- type: "string",
1199
- description: "The project ID or URL-encoded path"
1200
- },
1201
- pipeline_id: {
1202
- type: "number",
1203
- description: "The ID of the pipeline"
1204
- },
1205
- scope: {
1206
- type: "string",
1207
- enum: [
1208
- "created",
1209
- "pending",
1210
- "running",
1211
- "failed",
1212
- "success",
1213
- "canceled",
1214
- "skipped",
1215
- "manual"
1216
- ],
1217
- description: "Filter jobs by scope/status"
1218
- }
1219
- },
1220
- required: ["project_id", "pipeline_id"]
1221
- }
1222
- },
1223
- {
1224
- name: "gitlab_get_job_log",
1225
- description: `Get the log/trace output of a specific CI job.`,
1226
- input_schema: {
1227
- type: "object",
1228
- properties: {
1229
- project_id: {
1230
- type: "string",
1231
- description: "The project ID or URL-encoded path"
1232
- },
1233
- job_id: {
1234
- type: "number",
1235
- description: "The ID of the job"
1236
- }
1237
- },
1238
- required: ["project_id", "job_id"]
1239
- }
1240
- },
1241
- {
1242
- name: "gitlab_retry_job",
1243
- description: `Retry a failed or canceled CI job.`,
1244
- input_schema: {
1245
- type: "object",
1246
- properties: {
1247
- project_id: {
1248
- type: "string",
1249
- description: "The project ID or URL-encoded path"
1250
- },
1251
- job_id: {
1252
- type: "number",
1253
- description: "The ID of the job to retry"
1254
- }
1255
- },
1256
- required: ["project_id", "job_id"]
1257
- }
1258
- },
1259
- // Repository Tools
1260
- {
1261
- name: "gitlab_get_file",
1262
- description: `Get the contents of a file from a repository.`,
1263
- input_schema: {
1264
- type: "object",
1265
- properties: {
1266
- project_id: {
1267
- type: "string",
1268
- description: "The project ID or URL-encoded path"
1269
- },
1270
- file_path: {
1271
- type: "string",
1272
- description: "Path to the file in the repository"
1273
- },
1274
- ref: {
1275
- type: "string",
1276
- description: "Branch, tag, or commit SHA (default: default branch)"
1277
- }
1278
- },
1279
- required: ["project_id", "file_path"]
1280
- }
1281
- },
1282
- {
1283
- name: "gitlab_list_commits",
1284
- description: `List commits in a repository. Can filter by branch/ref and path.`,
1285
- input_schema: {
1286
- type: "object",
1287
- properties: {
1288
- project_id: {
1289
- type: "string",
1290
- description: "The project ID or URL-encoded path"
1291
- },
1292
- ref: {
1293
- type: "string",
1294
- description: "Branch or tag name"
1295
- },
1296
- path: {
1297
- type: "string",
1298
- description: "File or directory path to filter commits"
1299
- },
1300
- since: {
1301
- type: "string",
1302
- description: "Only commits after this date (ISO 8601 format)"
1303
- },
1304
- until: {
1305
- type: "string",
1306
- description: "Only commits before this date (ISO 8601 format)"
1307
- },
1308
- limit: {
1309
- type: "number",
1310
- description: "Maximum number of results (default: 20)"
1311
- }
1312
- },
1313
- required: ["project_id"]
1314
- }
1315
- },
1316
- {
1317
- name: "gitlab_get_commit_diff",
1318
- description: `Get the diff for a specific commit.`,
1319
- input_schema: {
1320
- type: "object",
1321
- properties: {
1322
- project_id: {
1323
- type: "string",
1324
- description: "The project ID or URL-encoded path"
1325
- },
1326
- sha: {
1327
- type: "string",
1328
- description: "The commit SHA"
1329
- }
1330
- },
1331
- required: ["project_id", "sha"]
1332
- }
1333
- },
1334
- {
1335
- name: "gitlab_list_branches",
1336
- description: `List branches in a repository.`,
1337
- input_schema: {
1338
- type: "object",
1339
- properties: {
1340
- project_id: {
1341
- type: "string",
1342
- description: "The project ID or URL-encoded path"
1343
- },
1344
- search: {
1345
- type: "string",
1346
- description: "Search branches by name"
1347
- }
1348
- },
1349
- required: ["project_id"]
1350
- }
1351
- },
1352
- // Search Tools
1353
- {
1354
- name: "gitlab_search",
1355
- description: `Search across GitLab for various resources.
1356
- Scopes: projects, issues, merge_requests, milestones, users, blobs (code), commits, notes, wiki_blobs`,
1357
- input_schema: {
1358
- type: "object",
1359
- properties: {
1360
- scope: {
1361
- type: "string",
1362
- enum: [
1363
- "projects",
1364
- "issues",
1365
- "merge_requests",
1366
- "milestones",
1367
- "users",
1368
- "blobs",
1369
- "commits",
1370
- "notes",
1371
- "wiki_blobs"
1372
- ],
1373
- description: "The scope of the search"
1374
- },
1375
- search: {
1376
- type: "string",
1377
- description: "The search query"
1378
- },
1379
- project_id: {
1380
- type: "string",
1381
- description: "Limit search to a specific project (optional)"
1382
- },
1383
- limit: {
1384
- type: "number",
1385
- description: "Maximum number of results (default: 20)"
1386
- }
1387
- },
1388
- required: ["scope", "search"]
1389
- }
1390
- },
1391
- // Project Tools
1392
- {
1393
- name: "gitlab_get_project",
1394
- description: `Get details of a specific project.`,
1395
- input_schema: {
1396
- type: "object",
1397
- properties: {
1398
- project_id: {
1399
- type: "string",
1400
- description: 'The project ID or URL-encoded path (e.g., "gitlab-org/gitlab")'
1401
- }
1402
- },
1403
- required: ["project_id"]
1404
- }
1405
- },
1406
- {
1407
- name: "gitlab_list_project_members",
1408
- description: `List members of a project.`,
1409
- input_schema: {
1410
- type: "object",
1411
- properties: {
1412
- project_id: {
1413
- type: "string",
1414
- description: "The project ID or URL-encoded path"
1415
- }
1416
- },
1417
- required: ["project_id"]
1418
- }
1419
- }
1420
- ];
1421
- var GitLabApiToolExecutor = class {
1422
- config;
1423
- constructor(config) {
1424
- this.config = config;
1425
- }
1426
- get headers() {
1427
- return {
1428
- Authorization: `Bearer ${this.config.token}`,
1429
- "Content-Type": "application/json"
1430
- };
1431
- }
1432
- async fetchApi(method, path4, body) {
1433
- const url = `${this.config.instanceUrl}/api/v4${path4}`;
1434
- const fetchFn = this.config.fetch || fetch;
1435
- const response = await fetchFn(url, {
1436
- method,
1437
- headers: this.headers,
1438
- body: body ? JSON.stringify(body) : void 0
1439
- });
1440
- if (!response.ok) {
1441
- const errorText = await response.text();
1442
- throw new Error(`GitLab API error ${response.status}: ${errorText}`);
1443
- }
1444
- const text = await response.text();
1445
- if (!text) {
1446
- return {};
1447
- }
1448
- return JSON.parse(text);
1449
- }
1450
- encodeProjectId(projectId) {
1451
- if (projectId.includes("/")) {
1452
- return encodeURIComponent(projectId);
1453
- }
1454
- return projectId;
1455
- }
1456
- /**
1457
- * Execute a GitLab API tool by name
1458
- */
1459
- async execute(toolName, input) {
1460
- try {
1461
- switch (toolName) {
1462
- // Merge Request tools
1463
- case "gitlab_get_merge_request":
1464
- return this.getMergeRequest(input);
1465
- case "gitlab_list_merge_requests":
1466
- return this.listMergeRequests(input);
1467
- case "gitlab_get_mr_changes":
1468
- return this.getMrChanges(input);
1469
- case "gitlab_list_mr_discussions":
1470
- return this.listMrDiscussions(input);
1471
- case "gitlab_create_mr_note":
1472
- return this.createMrNote(input);
1473
- // Issue tools
1474
- case "gitlab_get_issue":
1475
- return this.getIssue(input);
1476
- case "gitlab_list_issues":
1477
- return this.listIssues(input);
1478
- case "gitlab_create_issue_note":
1479
- return this.createIssueNote(input);
1480
- // Pipeline tools
1481
- case "gitlab_list_pipelines":
1482
- return this.listPipelines(input);
1483
- case "gitlab_get_pipeline":
1484
- return this.getPipeline(input);
1485
- case "gitlab_list_pipeline_jobs":
1486
- return this.listPipelineJobs(input);
1487
- case "gitlab_get_job_log":
1488
- return this.getJobLog(input);
1489
- case "gitlab_retry_job":
1490
- return this.retryJob(input);
1491
- // Repository tools
1492
- case "gitlab_get_file":
1493
- return this.getFile(input);
1494
- case "gitlab_list_commits":
1495
- return this.listCommits(input);
1496
- case "gitlab_get_commit_diff":
1497
- return this.getCommitDiff(input);
1498
- case "gitlab_list_branches":
1499
- return this.listBranches(input);
1500
- // Search tools
1501
- case "gitlab_search":
1502
- return this.search(input);
1503
- // Project tools
1504
- case "gitlab_get_project":
1505
- return this.getProject(input);
1506
- case "gitlab_list_project_members":
1507
- return this.listProjectMembers(input);
1508
- default:
1509
- return { result: "", error: `Unknown GitLab tool: ${toolName}` };
1510
- }
1511
- } catch (error) {
1512
- return {
1513
- result: "",
1514
- error: error instanceof Error ? error.message : String(error)
1515
- };
1516
- }
1517
- }
1518
- // ========== Merge Request Tools ==========
1519
- async getMergeRequest(input) {
1520
- const projectId = this.encodeProjectId(input.project_id);
1521
- const mrIid = input.mr_iid;
1522
- const includeChanges = input.include_changes;
1523
- let path4 = `/projects/${projectId}/merge_requests/${mrIid}`;
1524
- if (includeChanges) {
1525
- path4 += "?include_diverged_commits_count=true";
1526
- }
1527
- const mr = await this.fetchApi("GET", path4);
1528
- return { result: JSON.stringify(mr, null, 2) };
1529
- }
1530
- async listMergeRequests(input) {
1531
- const params = new URLSearchParams();
1532
- params.set("per_page", String(input.limit || 20));
1533
- if (input.state) params.set("state", input.state);
1534
- if (input.scope) params.set("scope", input.scope);
1535
- if (input.search) params.set("search", input.search);
1536
- if (input.labels) params.set("labels", input.labels);
1537
- let path4;
1538
- if (input.project_id) {
1539
- const projectId = this.encodeProjectId(input.project_id);
1540
- path4 = `/projects/${projectId}/merge_requests?${params}`;
1541
- } else {
1542
- path4 = `/merge_requests?${params}`;
1543
- }
1544
- const mrs = await this.fetchApi("GET", path4);
1545
- return { result: JSON.stringify(mrs, null, 2) };
1546
- }
1547
- async getMrChanges(input) {
1548
- const projectId = this.encodeProjectId(input.project_id);
1549
- const mrIid = input.mr_iid;
1550
- const changes = await this.fetchApi(
1551
- "GET",
1552
- `/projects/${projectId}/merge_requests/${mrIid}/changes`
1553
- );
1554
- return { result: JSON.stringify(changes, null, 2) };
1555
- }
1556
- async listMrDiscussions(input) {
1557
- const projectId = this.encodeProjectId(input.project_id);
1558
- const mrIid = input.mr_iid;
1559
- const discussions = await this.fetchApi(
1560
- "GET",
1561
- `/projects/${projectId}/merge_requests/${mrIid}/discussions`
1562
- );
1563
- return { result: JSON.stringify(discussions, null, 2) };
1564
- }
1565
- async createMrNote(input) {
1566
- const projectId = this.encodeProjectId(input.project_id);
1567
- const mrIid = input.mr_iid;
1568
- const body = input.body;
1569
- const note = await this.fetchApi(
1570
- "POST",
1571
- `/projects/${projectId}/merge_requests/${mrIid}/notes`,
1572
- { body }
1573
- );
1574
- return { result: JSON.stringify(note, null, 2) };
1575
- }
1576
- // ========== Issue Tools ==========
1577
- async getIssue(input) {
1578
- const projectId = this.encodeProjectId(input.project_id);
1579
- const issueIid = input.issue_iid;
1580
- const issue = await this.fetchApi(
1581
- "GET",
1582
- `/projects/${projectId}/issues/${issueIid}`
1583
- );
1584
- return { result: JSON.stringify(issue, null, 2) };
1585
- }
1586
- async listIssues(input) {
1587
- const params = new URLSearchParams();
1588
- params.set("per_page", String(input.limit || 20));
1589
- if (input.state) params.set("state", input.state);
1590
- if (input.scope) params.set("scope", input.scope);
1591
- if (input.search) params.set("search", input.search);
1592
- if (input.labels) params.set("labels", input.labels);
1593
- if (input.milestone) params.set("milestone", input.milestone);
1594
- let path4;
1595
- if (input.project_id) {
1596
- const projectId = this.encodeProjectId(input.project_id);
1597
- path4 = `/projects/${projectId}/issues?${params}`;
1598
- } else {
1599
- path4 = `/issues?${params}`;
1600
- }
1601
- const issues = await this.fetchApi("GET", path4);
1602
- return { result: JSON.stringify(issues, null, 2) };
1603
- }
1604
- async createIssueNote(input) {
1605
- const projectId = this.encodeProjectId(input.project_id);
1606
- const issueIid = input.issue_iid;
1607
- const body = input.body;
1608
- const note = await this.fetchApi(
1609
- "POST",
1610
- `/projects/${projectId}/issues/${issueIid}/notes`,
1611
- { body }
1612
- );
1613
- return { result: JSON.stringify(note, null, 2) };
1614
- }
1615
- // ========== Pipeline Tools ==========
1616
- async listPipelines(input) {
1617
- const projectId = this.encodeProjectId(input.project_id);
1618
- const params = new URLSearchParams();
1619
- params.set("per_page", String(input.limit || 20));
1620
- if (input.status) params.set("status", input.status);
1621
- if (input.ref) params.set("ref", input.ref);
1622
- const pipelines = await this.fetchApi(
1623
- "GET",
1624
- `/projects/${projectId}/pipelines?${params}`
1625
- );
1626
- return { result: JSON.stringify(pipelines, null, 2) };
1627
- }
1628
- async getPipeline(input) {
1629
- const projectId = this.encodeProjectId(input.project_id);
1630
- const pipelineId = input.pipeline_id;
1631
- const pipeline = await this.fetchApi(
1632
- "GET",
1633
- `/projects/${projectId}/pipelines/${pipelineId}`
1634
- );
1635
- return { result: JSON.stringify(pipeline, null, 2) };
1636
- }
1637
- async listPipelineJobs(input) {
1638
- const projectId = this.encodeProjectId(input.project_id);
1639
- const pipelineId = input.pipeline_id;
1640
- const params = new URLSearchParams();
1641
- if (input.scope) params.set("scope[]", input.scope);
1642
- const jobs = await this.fetchApi(
1643
- "GET",
1644
- `/projects/${projectId}/pipelines/${pipelineId}/jobs?${params}`
1645
- );
1646
- return { result: JSON.stringify(jobs, null, 2) };
1647
- }
1648
- async getJobLog(input) {
1649
- const projectId = this.encodeProjectId(input.project_id);
1650
- const jobId = input.job_id;
1651
- const url = `${this.config.instanceUrl}/api/v4/projects/${projectId}/jobs/${jobId}/trace`;
1652
- const fetchFn = this.config.fetch || fetch;
1653
- const response = await fetchFn(url, {
1654
- method: "GET",
1655
- headers: this.headers
1656
- });
1657
- if (!response.ok) {
1658
- const errorText = await response.text();
1659
- throw new Error(`GitLab API error ${response.status}: ${errorText}`);
1660
- }
1661
- const log = await response.text();
1662
- const maxLength = 5e4;
1663
- if (log.length > maxLength) {
1664
- return {
1665
- result: `[Log truncated, showing last ${maxLength} characters]
1666
-
1667
- ${log.slice(-maxLength)}`
1668
- };
1669
- }
1670
- return { result: log };
1671
- }
1672
- async retryJob(input) {
1673
- const projectId = this.encodeProjectId(input.project_id);
1674
- const jobId = input.job_id;
1675
- const job = await this.fetchApi(
1676
- "POST",
1677
- `/projects/${projectId}/jobs/${jobId}/retry`
1678
- );
1679
- return { result: JSON.stringify(job, null, 2) };
1680
- }
1681
- // ========== Repository Tools ==========
1682
- async getFile(input) {
1683
- const projectId = this.encodeProjectId(input.project_id);
1684
- const filePath = encodeURIComponent(input.file_path);
1685
- const ref = input.ref || "HEAD";
1686
- const file = await this.fetchApi(
1687
- "GET",
1688
- `/projects/${projectId}/repository/files/${filePath}?ref=${encodeURIComponent(ref)}`
1689
- );
1690
- if (file.encoding === "base64") {
1691
- const decoded = Buffer.from(file.content, "base64").toString("utf-8");
1692
- return { result: decoded };
1693
- }
1694
- return { result: file.content };
1695
- }
1696
- async listCommits(input) {
1697
- const projectId = this.encodeProjectId(input.project_id);
1698
- const params = new URLSearchParams();
1699
- params.set("per_page", String(input.limit || 20));
1700
- if (input.ref) params.set("ref_name", input.ref);
1701
- if (input.path) params.set("path", input.path);
1702
- if (input.since) params.set("since", input.since);
1703
- if (input.until) params.set("until", input.until);
1704
- const commits = await this.fetchApi(
1705
- "GET",
1706
- `/projects/${projectId}/repository/commits?${params}`
1707
- );
1708
- return { result: JSON.stringify(commits, null, 2) };
1709
- }
1710
- async getCommitDiff(input) {
1711
- const projectId = this.encodeProjectId(input.project_id);
1712
- const sha = input.sha;
1713
- const diff = await this.fetchApi(
1714
- "GET",
1715
- `/projects/${projectId}/repository/commits/${sha}/diff`
1716
- );
1717
- return { result: JSON.stringify(diff, null, 2) };
1718
- }
1719
- async listBranches(input) {
1720
- const projectId = this.encodeProjectId(input.project_id);
1721
- const params = new URLSearchParams();
1722
- if (input.search) params.set("search", input.search);
1723
- const branches = await this.fetchApi(
1724
- "GET",
1725
- `/projects/${projectId}/repository/branches?${params}`
1726
- );
1727
- return { result: JSON.stringify(branches, null, 2) };
1728
- }
1729
- // ========== Search Tools ==========
1730
- async search(input) {
1731
- const scope = input.scope;
1732
- const searchQuery = input.search;
1733
- const params = new URLSearchParams();
1734
- params.set("scope", scope);
1735
- params.set("search", searchQuery);
1736
- params.set("per_page", String(input.limit || 20));
1737
- let path4;
1738
- if (input.project_id) {
1739
- const projectId = this.encodeProjectId(input.project_id);
1740
- path4 = `/projects/${projectId}/search?${params}`;
1741
- } else {
1742
- path4 = `/search?${params}`;
1743
- }
1744
- const results = await this.fetchApi("GET", path4);
1745
- return { result: JSON.stringify(results, null, 2) };
1746
- }
1747
- // ========== Project Tools ==========
1748
- async getProject(input) {
1749
- const projectId = this.encodeProjectId(input.project_id);
1750
- const project = await this.fetchApi("GET", `/projects/${projectId}`);
1751
- return { result: JSON.stringify(project, null, 2) };
1752
- }
1753
- async listProjectMembers(input) {
1754
- const projectId = this.encodeProjectId(input.project_id);
1755
- const members = await this.fetchApi(
1756
- "GET",
1757
- `/projects/${projectId}/members`
1758
- );
1759
- return { result: JSON.stringify(members, null, 2) };
1760
- }
1761
- };
1762
- function isGitLabApiTool(toolName) {
1763
- return toolName.startsWith("gitlab_");
1764
- }
1765
-
1766
- // src/gitlab-anthropic-tools.ts
1767
- var fs2 = __toESM(require("fs/promises"));
1768
- var path2 = __toESM(require("path"));
1769
- var import_child_process = require("child_process");
1770
- var ANTHROPIC_TOOLS = [
1771
- {
1772
- name: "list_dir",
1773
- description: `List directory contents. Shows files and subdirectories relative to the working directory.`,
1774
- input_schema: {
1775
- type: "object",
1776
- properties: {
1777
- directory: {
1778
- type: "string",
1779
- description: "Directory path relative to the working directory"
1780
- }
1781
- },
1782
- required: ["directory"]
1783
- }
1784
- },
1785
- {
1786
- name: "read_file",
1787
- description: `Read the contents of a file.`,
1788
- input_schema: {
1789
- type: "object",
1790
- properties: {
1791
- file_path: {
1792
- type: "string",
1793
- description: "The file path to read"
1794
- }
1795
- },
1796
- required: ["file_path"]
1797
- }
1798
- },
1799
- {
1800
- name: "create_file_with_contents",
1801
- description: `Create and write contents to a file.`,
1802
- input_schema: {
1803
- type: "object",
1804
- properties: {
1805
- file_path: {
1806
- type: "string",
1807
- description: "The file path to write to"
1808
- },
1809
- contents: {
1810
- type: "string",
1811
- description: "The contents to write"
1812
- }
1813
- },
1814
- required: ["file_path", "contents"]
1815
- }
1816
- },
1817
- {
1818
- name: "edit_file",
1819
- description: `Edit an existing file by replacing a string with a new string.`,
1820
- input_schema: {
1821
- type: "object",
1822
- properties: {
1823
- file_path: {
1824
- type: "string",
1825
- description: "The path of the file to edit"
1826
- },
1827
- old_str: {
1828
- type: "string",
1829
- description: "The string to replace (include context for uniqueness)"
1830
- },
1831
- new_str: {
1832
- type: "string",
1833
- description: "The new string value"
1834
- }
1835
- },
1836
- required: ["file_path", "old_str", "new_str"]
1837
- }
1838
- },
1839
- {
1840
- name: "find_files",
1841
- description: `Find files by name pattern. Uses glob-like matching.`,
1842
- input_schema: {
1843
- type: "object",
1844
- properties: {
1845
- name_pattern: {
1846
- type: "string",
1847
- description: 'The pattern to search for (e.g., "*.py", "test_*.js")'
1848
- }
1849
- },
1850
- required: ["name_pattern"]
1851
- }
1852
- },
1853
- {
1854
- name: "mkdir",
1855
- description: `Create a new directory.`,
1856
- input_schema: {
1857
- type: "object",
1858
- properties: {
1859
- directory_path: {
1860
- type: "string",
1861
- description: "The directory path to create"
1862
- }
1863
- },
1864
- required: ["directory_path"]
1865
- }
1866
- },
1867
- {
1868
- name: "grep",
1869
- description: `Search for text patterns within files.`,
1870
- input_schema: {
1871
- type: "object",
1872
- properties: {
1873
- pattern: {
1874
- type: "string",
1875
- description: "The text pattern to search for"
1876
- },
1877
- search_directory: {
1878
- type: "string",
1879
- description: 'The directory to search in (default: ".")'
1880
- },
1881
- case_insensitive: {
1882
- type: "boolean",
1883
- description: "Whether to ignore case (default: false)"
1884
- }
1885
- },
1886
- required: ["pattern"]
1887
- }
1888
- },
1889
- {
1890
- name: "run_command",
1891
- description: `Run a shell command. Note: git commands should use run_git_command instead.`,
1892
- input_schema: {
1893
- type: "object",
1894
- properties: {
1895
- program: {
1896
- type: "string",
1897
- description: 'The program to execute (e.g., "npm", "python")'
1898
- },
1899
- args: {
1900
- type: "string",
1901
- description: "Arguments as a single string"
1902
- }
1903
- },
1904
- required: ["program"]
1905
- }
1906
- },
1907
- {
1908
- name: "run_git_command",
1909
- description: `Run a git command in the repository.`,
1910
- input_schema: {
1911
- type: "object",
1912
- properties: {
1913
- command: {
1914
- type: "string",
1915
- description: 'Git command (e.g., "status", "log", "diff")'
1916
- },
1917
- args: {
1918
- type: "string",
1919
- description: "Git command arguments"
1920
- }
1921
- },
1922
- required: ["command"]
1923
- }
1924
- }
1925
- ];
1926
- var AnthropicToolExecutor = class {
1927
- workingDirectory;
1928
- constructor(workingDirectory) {
1929
- this.workingDirectory = workingDirectory;
1930
- }
1931
- /**
1932
- * Execute a tool by name with given input
1933
- */
1934
- async execute(toolName, input) {
1935
- try {
1936
- switch (toolName) {
1937
- case "list_dir":
1938
- return this.listDir(input.directory);
1939
- case "read_file":
1940
- return this.readFile(input.file_path);
1941
- case "create_file_with_contents":
1942
- return this.writeFile(input.file_path, input.contents);
1943
- case "edit_file":
1944
- return this.editFile(
1945
- input.file_path,
1946
- input.old_str,
1947
- input.new_str
1948
- );
1949
- case "find_files":
1950
- return this.findFiles(input.name_pattern);
1951
- case "mkdir":
1952
- return this.mkdir(input.directory_path);
1953
- case "grep":
1954
- return this.grep(
1955
- input.pattern,
1956
- input.search_directory,
1957
- input.case_insensitive
1958
- );
1959
- case "run_command":
1960
- return this.runCommand(input.program, input.args);
1961
- case "run_git_command":
1962
- return this.runGitCommand(input.command, input.args);
1963
- default:
1964
- return { result: "", error: `Unknown tool: ${toolName}` };
1965
- }
1966
- } catch (error) {
1967
- return {
1968
- result: "",
1969
- error: error instanceof Error ? error.message : String(error)
1970
- };
1971
- }
1972
- }
1973
- resolvePath(filePath) {
1974
- if (path2.isAbsolute(filePath)) {
1975
- return filePath;
1976
- }
1977
- return path2.resolve(this.workingDirectory, filePath);
1978
- }
1979
- async listDir(directory) {
1980
- const dirPath = this.resolvePath(directory || ".");
1981
- const entries = await fs2.readdir(dirPath, { withFileTypes: true });
1982
- const result = entries.map((entry) => {
1983
- const type = entry.isDirectory() ? "d" : "-";
1984
- return `${type} ${entry.name}`;
1985
- }).join("\n");
1986
- return { result };
1987
- }
1988
- async readFile(filePath) {
1989
- const fullPath = this.resolvePath(filePath);
1990
- const content = await fs2.readFile(fullPath, "utf-8");
1991
- return { result: content };
1992
- }
1993
- async writeFile(filePath, contents) {
1994
- const fullPath = this.resolvePath(filePath);
1995
- await fs2.mkdir(path2.dirname(fullPath), { recursive: true });
1996
- await fs2.writeFile(fullPath, contents, "utf-8");
1997
- return { result: `File written successfully: ${filePath}` };
1998
- }
1999
- async editFile(filePath, oldStr, newStr) {
2000
- const fullPath = this.resolvePath(filePath);
2001
- const content = await fs2.readFile(fullPath, "utf-8");
2002
- if (!content.includes(oldStr)) {
2003
- return { result: "", error: `String not found in file: "${oldStr.substring(0, 50)}..."` };
2004
- }
2005
- const newContent = content.replace(oldStr, newStr);
2006
- await fs2.writeFile(fullPath, newContent, "utf-8");
2007
- return { result: `File edited successfully: ${filePath}` };
2008
- }
2009
- async findFiles(namePattern) {
2010
- const results = [];
2011
- const regex = new RegExp("^" + namePattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$");
2012
- const search = async (dir, relativePath = "") => {
2013
- try {
2014
- const entries = await fs2.readdir(dir, { withFileTypes: true });
2015
- for (const entry of entries) {
2016
- const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
2017
- if (entry.isDirectory()) {
2018
- if (!["node_modules", ".git", "dist", "build", "__pycache__"].includes(entry.name)) {
2019
- await search(path2.join(dir, entry.name), entryRelativePath);
2020
- }
2021
- } else if (regex.test(entry.name)) {
2022
- results.push(entryRelativePath);
2023
- }
2024
- }
2025
- } catch {
2026
- }
2027
- };
2028
- await search(this.workingDirectory);
2029
- return { result: results.join("\n") || "No files found" };
2030
- }
2031
- async mkdir(directoryPath) {
2032
- const fullPath = this.resolvePath(directoryPath);
2033
- await fs2.mkdir(fullPath, { recursive: true });
2034
- return { result: `Directory created: ${directoryPath}` };
2035
- }
2036
- async grep(pattern, searchDirectory, caseInsensitive) {
2037
- const results = [];
2038
- const flags = caseInsensitive ? "gi" : "g";
2039
- const regex = new RegExp(pattern, flags);
2040
- const searchDir = this.resolvePath(searchDirectory || ".");
2041
- const search = async (dir, relativePath = "") => {
2042
- try {
2043
- const entries = await fs2.readdir(dir, { withFileTypes: true });
2044
- for (const entry of entries) {
2045
- const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
2046
- const fullPath = path2.join(dir, entry.name);
2047
- if (entry.isDirectory()) {
2048
- if (!["node_modules", ".git", "dist", "build", "__pycache__"].includes(entry.name)) {
2049
- await search(fullPath, entryRelativePath);
2050
- }
2051
- } else {
2052
- try {
2053
- const content = await fs2.readFile(fullPath, "utf-8");
2054
- const lines = content.split("\n");
2055
- for (let i = 0; i < lines.length; i++) {
2056
- if (regex.test(lines[i])) {
2057
- results.push(`${entryRelativePath}:${i + 1}: ${lines[i].trim()}`);
2058
- }
2059
- }
2060
- } catch {
2061
- }
2062
- }
2063
- }
2064
- } catch {
2065
- }
2066
- };
2067
- await search(searchDir);
2068
- return { result: results.slice(0, 100).join("\n") || "No matches found" };
2069
- }
2070
- runCommand(program, args) {
2071
- if (program === "git") {
2072
- return Promise.resolve({
2073
- result: "",
2074
- error: "Use run_git_command for git operations"
2075
- });
2076
- }
2077
- const parsedArgs = args ? args.match(/(?:[^\s"]+|"[^"]*")+/g) || [] : [];
2078
- const cleanedArgs = parsedArgs.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
2079
- return this.executeCommand(program, cleanedArgs);
2080
- }
2081
- runGitCommand(command, args) {
2082
- const gitArgs = [command];
2083
- if (args) {
2084
- const parsedArgs = args.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
2085
- gitArgs.push(...parsedArgs.map((arg) => arg.replace(/^"(.*)"$/, "$1")));
2086
- }
2087
- return this.executeCommand("git", gitArgs);
2088
- }
2089
- executeCommand(program, args) {
2090
- return new Promise((resolve3) => {
2091
- const child = (0, import_child_process.spawn)(program, args, {
2092
- cwd: this.workingDirectory,
2093
- timeout: 3e4
2094
- });
2095
- let stdout = "";
2096
- let stderr = "";
2097
- child.stdout?.on("data", (data) => {
2098
- stdout += data.toString();
2099
- });
2100
- child.stderr?.on("data", (data) => {
2101
- stderr += data.toString();
2102
- });
2103
- child.on("error", (error) => {
2104
- resolve3({
2105
- result: "",
2106
- error: `Command failed: ${error.message}`
2107
- });
2108
- });
2109
- child.on("close", (exitCode) => {
2110
- if (exitCode !== 0 && stderr) {
2111
- resolve3({
2112
- result: stdout,
2113
- error: stderr
2114
- });
2115
- } else {
2116
- resolve3({
2117
- result: stdout || stderr || `Command completed with exit code ${exitCode}`
2118
- });
2119
- }
2120
- });
2121
- });
2122
- }
2123
- };
2124
-
2125
995
  // src/gitlab-project-detector.ts
2126
- var import_child_process2 = require("child_process");
2127
- var path3 = __toESM(require("path"));
996
+ var import_child_process = require("child_process");
997
+ var path2 = __toESM(require("path"));
2128
998
 
2129
999
  // src/gitlab-project-cache.ts
2130
1000
  var GitLabProjectCache = class {
@@ -2230,7 +1100,7 @@ var GitLabProjectDetector = class {
2230
1100
  * @returns The detected project or null if detection fails
2231
1101
  */
2232
1102
  async detectProject(workingDirectory, remoteName = "origin") {
2233
- const cacheKey = path3.resolve(workingDirectory);
1103
+ const cacheKey = path2.resolve(workingDirectory);
2234
1104
  const cached = this.cache.get(cacheKey);
2235
1105
  if (cached) {
2236
1106
  return cached;
@@ -2315,8 +1185,8 @@ var GitLabProjectDetector = class {
2315
1185
  * @returns The remote URL or null if not found
2316
1186
  */
2317
1187
  async getGitRemoteUrl(workingDirectory, remoteName = "origin") {
2318
- return new Promise((resolve3) => {
2319
- const child = (0, import_child_process2.spawn)("git", ["config", "--get", `remote.${remoteName}.url`], {
1188
+ return new Promise((resolve2) => {
1189
+ const child = (0, import_child_process.spawn)("git", ["config", "--get", `remote.${remoteName}.url`], {
2320
1190
  cwd: workingDirectory,
2321
1191
  timeout: this.config.gitTimeout
2322
1192
  });
@@ -2330,13 +1200,13 @@ var GitLabProjectDetector = class {
2330
1200
  });
2331
1201
  child.on("close", (exitCode) => {
2332
1202
  if (exitCode === 0 && stdout.trim()) {
2333
- resolve3(stdout.trim());
1203
+ resolve2(stdout.trim());
2334
1204
  } else {
2335
- resolve3(null);
1205
+ resolve2(null);
2336
1206
  }
2337
1207
  });
2338
1208
  child.on("error", () => {
2339
- resolve3(null);
1209
+ resolve2(null);
2340
1210
  });
2341
1211
  });
2342
1212
  }
@@ -2393,13 +1263,11 @@ var GitLabProjectDetector = class {
2393
1263
  };
2394
1264
  // Annotate the CommonJS export names for ESM import in node:
2395
1265
  0 && (module.exports = {
2396
- ANTHROPIC_TOOLS,
2397
- AnthropicToolExecutor,
2398
1266
  BUNDLED_CLIENT_ID,
2399
- GITLAB_API_TOOLS,
1267
+ DEFAULT_AI_GATEWAY_URL,
2400
1268
  GITLAB_COM_URL,
2401
1269
  GitLabAgenticLanguageModel,
2402
- GitLabApiToolExecutor,
1270
+ GitLabDirectAccessClient,
2403
1271
  GitLabError,
2404
1272
  GitLabOAuthManager,
2405
1273
  GitLabProjectCache,
@@ -2409,7 +1277,6 @@ var GitLabProjectDetector = class {
2409
1277
  TOKEN_EXPIRY_SKEW_MS,
2410
1278
  createGitLab,
2411
1279
  getAnthropicModelForModelId,
2412
- gitlab,
2413
- isGitLabApiTool
1280
+ gitlab
2414
1281
  });
2415
1282
  //# sourceMappingURL=index.js.map