ai-dev-requirements 0.1.7 → 0.1.9

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.mjs CHANGED
@@ -95,7 +95,7 @@ const SEARCH_TASKS_QUERY = `
95
95
  key
96
96
  tasks(filterGroup: $filterGroup, orderBy: $orderBy, limit: $limit, includeAncestors: { pathField: "path" }) {
97
97
  key uuid number name
98
- issueType { uuid name }
98
+ issueType { uuid name detailType }
99
99
  status { uuid name category }
100
100
  priority { value }
101
101
  assign { uuid name }
@@ -104,6 +104,15 @@ const SEARCH_TASKS_QUERY = `
104
104
  }
105
105
  }
106
106
  `;
107
+ const ISSUE_TYPES_QUERY = `
108
+ query IssueTypes($orderBy: OrderBy) {
109
+ issueTypes(orderBy: $orderBy) {
110
+ uuid
111
+ name
112
+ detailType
113
+ }
114
+ }
115
+ `;
107
116
  const TASK_BY_NUMBER_QUERY = SEARCH_TASKS_QUERY;
108
117
  const RELATED_TASKS_QUERY = `
109
118
  query Task($key: Key) {
@@ -165,6 +174,117 @@ const DEFAULT_STATUS_NOT_IN = [
165
174
  "Dn3k8ffK",
166
175
  "TbmY2So5"
167
176
  ];
177
+ const TESTCASE_LIBRARY_LIST_QUERY = `
178
+ query Q {
179
+ testcaseLibraries {
180
+ uuid name key
181
+ testcaseCaseCount
182
+ }
183
+ }
184
+ `;
185
+ const TESTCASE_MODULE_SEARCH_QUERY = `
186
+ query Q($filter: Filter) {
187
+ testcaseModules(filter: $filter) {
188
+ uuid name key
189
+ parent { uuid name }
190
+ }
191
+ }
192
+ `;
193
+ const TESTCASE_LIST_PAGED_QUERY = `
194
+ query PAGED_LIBRARY_TESTCASE_LIST($testCaseFilter: Filter, $pagination: Pagination) {
195
+ buckets(groupBy: {testcaseCases: {}}, pagination: $pagination) {
196
+ testcaseCases(filterGroup: $testCaseFilter, limit: 10000) {
197
+ uuid name key id
198
+ priority { uuid value }
199
+ type { uuid value }
200
+ assign { uuid name }
201
+ testcaseModule { uuid }
202
+ }
203
+ key
204
+ pageInfo { count totalCount hasNextPage endCursor }
205
+ }
206
+ }
207
+ `;
208
+ const TESTCASE_DETAIL_QUERY = `
209
+ query QUERY_TESTCASES_DETAIL($testCaseFilter: Filter, $stepFilter: Filter) {
210
+ testcaseCases(filter: $testCaseFilter) {
211
+ uuid name key id condition desc path
212
+ assign { uuid name }
213
+ priority { uuid value }
214
+ type { uuid value }
215
+ testcaseLibrary { uuid }
216
+ testcaseModule { uuid }
217
+ relatedTasks { uuid name number }
218
+ }
219
+ testcaseCaseSteps(filter: $stepFilter, orderBy: { index: ASC }) {
220
+ key uuid
221
+ testcaseCase { uuid }
222
+ desc result index
223
+ }
224
+ }
225
+ `;
226
+ function parseOnesSearchIntent(query) {
227
+ if (!query) return "keyword";
228
+ const normalized = query.toLowerCase();
229
+ if (query.includes("缺陷") || normalized.includes("bug")) return "all_bugs";
230
+ if (query.includes("任务")) return "all_tasks";
231
+ return "keyword";
232
+ }
233
+ function extractAssigneeName(query, intent) {
234
+ if (intent === "keyword") return null;
235
+ const trimmed = query.trim();
236
+ if (!trimmed) return null;
237
+ const ownerStyleMatch = trimmed.match(/\u8D1F\u8D23\u4EBA\u4E3A(.+?)\u7684?(?:\u7F3A\u9677|bug)$/i);
238
+ if (ownerStyleMatch?.[1]) return ownerStyleMatch[1].trim();
239
+ const candidate = trimmed.match(/^(查询)?(.+?)的(?:缺陷|bug|任务)$/i)?.[2]?.trim();
240
+ if (!candidate || candidate.includes("我")) return null;
241
+ return candidate;
242
+ }
243
+ function extractNamedAssignee(query, intent) {
244
+ if (intent === "keyword") return null;
245
+ const compact = query.replace(/\s+/g, "").trim();
246
+ if (!compact) return null;
247
+ const ownerStyleMatch = compact.match(/(?:\u8D1F\u8D23\u4EBA\u4E3A|\u8D1F\u8D23\u4EBA\u662F|\u6307\u6D3E\u7ED9|\u5206\u914D\u7ED9)(.+?)\u7684?(?:\u7F3A\u9677|bug|\u4EFB\u52A1)$/i);
248
+ if (ownerStyleMatch?.[1]) return ownerStyleMatch[1].trim();
249
+ const candidate = compact.match(/^(?:\u67E5\u8BE2|\u67E5\u627E|\u641C\u7D22)?(.+?)\u7684?(?:\u7F3A\u9677|bug|\u4EFB\u52A1)$/i)?.[1]?.trim();
250
+ if (!candidate || candidate.startsWith("我") || /^(?:\u6211|\u6211\u7684|\u6211\u6240\u6709|\u6211\u5168\u90E8|\u672C\u4EBA|\u5F53\u524D\u7528\u6237)$/.test(candidate)) return null;
251
+ return candidate;
252
+ }
253
+ function getBugStatusPriority(task) {
254
+ if (task.status?.category === "to_do") return 0;
255
+ if (task.status?.category === "in_progress") return 1;
256
+ return Number.POSITIVE_INFINITY;
257
+ }
258
+ function isOpenOrInProgressBug(task) {
259
+ const category = task.status?.category;
260
+ return category === "to_do" || category === "in_progress";
261
+ }
262
+ function extractTeamUsers(payload) {
263
+ const record = payload && typeof payload === "object" ? payload : null;
264
+ if (!record) return [];
265
+ const rawUsers = [
266
+ record.users,
267
+ record.items,
268
+ record.list,
269
+ record.results,
270
+ record.data?.users,
271
+ record.data?.items,
272
+ record.data?.list,
273
+ record.data?.results
274
+ ].find(Array.isArray);
275
+ if (!rawUsers) return [];
276
+ return rawUsers.map((item) => {
277
+ const user = item && typeof item === "object" ? item : null;
278
+ if (!user) return null;
279
+ const uuid = user.uuid ?? user.user?.uuid ?? user.orgUser?.uuid ?? user.orgUserUuid ?? user.org_user_uuid ?? user.org_user?.org_user_uuid;
280
+ const name = user.name ?? user.user?.name ?? user.orgUser?.name ?? user.org_user?.name;
281
+ if (!uuid || !name) return null;
282
+ return {
283
+ uuid,
284
+ name
285
+ };
286
+ }).filter((item) => item !== null);
287
+ }
168
288
  function base64Url(buffer) {
169
289
  return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
170
290
  }
@@ -195,6 +315,7 @@ function toRequirement(task, description = "") {
195
315
  }
196
316
  var OnesAdapter = class extends BaseAdapter {
197
317
  session = null;
318
+ issueTypesCache = null;
198
319
  constructor(sourceType, config, resolvedAuth) {
199
320
  super(sourceType, config, resolvedAuth);
200
321
  }
@@ -355,6 +476,42 @@ var OnesAdapter = class extends BaseAdapter {
355
476
  }
356
477
  return response.json();
357
478
  }
479
+ async fetchIssueTypes() {
480
+ if (this.issueTypesCache) return this.issueTypesCache;
481
+ this.issueTypesCache = (await this.graphql(ISSUE_TYPES_QUERY, { orderBy: { namePinyin: "ASC" } }, "issueTypes")).data?.issueTypes ?? [];
482
+ return this.issueTypesCache;
483
+ }
484
+ async searchTeamUsers(keyword) {
485
+ const session = await this.login();
486
+ const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/users/search`;
487
+ const response = await fetch(url, {
488
+ method: "POST",
489
+ headers: {
490
+ "Authorization": `Bearer ${session.accessToken}`,
491
+ "Content-Type": "application/json"
492
+ },
493
+ body: JSON.stringify({
494
+ keyword,
495
+ status: [1],
496
+ team_member_status: [1, 4],
497
+ types: [1, 10]
498
+ })
499
+ });
500
+ if (!response.ok) {
501
+ const text = await response.text().catch(() => "");
502
+ throw new Error(`ONES user search error: ${response.status} ${text}`);
503
+ }
504
+ return extractTeamUsers(await response.json());
505
+ }
506
+ async resolveAssigneeUuid(name) {
507
+ const trimmed = name.trim();
508
+ if (!trimmed) return null;
509
+ const users = await this.searchTeamUsers(trimmed);
510
+ const exactMatch = users.find((user) => user.name === trimmed);
511
+ if (exactMatch) return exactMatch.uuid;
512
+ const normalizedTarget = trimmed.toLowerCase();
513
+ return users.find((user) => user.name.toLowerCase().includes(normalizedTarget))?.uuid ?? null;
514
+ }
358
515
  /**
359
516
  * Fetch task info via REST API (includes description/rich fields not available in GraphQL).
360
517
  * Reference: ones/packages/core/src/tasks.ts → fetchTaskInfo
@@ -519,6 +676,27 @@ var OnesAdapter = class extends BaseAdapter {
519
676
  async searchRequirements(params) {
520
677
  const page = params.page ?? 1;
521
678
  const pageSize = params.pageSize ?? 50;
679
+ const intent = parseOnesSearchIntent(params.query);
680
+ const assigneeName = extractNamedAssignee(params.query, intent) ?? extractAssigneeName(params.query, intent);
681
+ const assigneeUuid = assigneeName ? await this.resolveAssigneeUuid(assigneeName) : null;
682
+ if (assigneeName && !assigneeUuid) return {
683
+ items: [],
684
+ total: 0,
685
+ page,
686
+ pageSize
687
+ };
688
+ let bugTypeUuids = [];
689
+ let taskTypeUuids = [];
690
+ if (intent === "all_bugs" || intent === "all_tasks") {
691
+ const issueTypes = await this.fetchIssueTypes();
692
+ bugTypeUuids = issueTypes.filter((item) => item.detailType === 3).map((item) => item.uuid);
693
+ taskTypeUuids = issueTypes.filter((item) => item.detailType === 2).map((item) => item.uuid);
694
+ }
695
+ const filter = { status_notIn: DEFAULT_STATUS_NOT_IN };
696
+ if (assigneeName) filter.assign_in = [assigneeUuid];
697
+ else filter.assign_in = ["${currentUser}"];
698
+ if (intent === "all_bugs") filter.issueType_in = bugTypeUuids;
699
+ if (intent === "all_tasks") filter.issueType_in = taskTypeUuids;
522
700
  let tasks = (await this.graphql(SEARCH_TASKS_QUERY, {
523
701
  groupBy: { tasks: {} },
524
702
  groupOrderBy: null,
@@ -526,10 +704,7 @@ var OnesAdapter = class extends BaseAdapter {
526
704
  position: "ASC",
527
705
  createTime: "DESC"
528
706
  },
529
- filterGroup: [{
530
- assign_in: ["${currentUser}"],
531
- status_notIn: DEFAULT_STATUS_NOT_IN
532
- }],
707
+ filterGroup: [filter],
533
708
  search: null,
534
709
  pagination: {
535
710
  limit: pageSize * page,
@@ -537,8 +712,11 @@ var OnesAdapter = class extends BaseAdapter {
537
712
  },
538
713
  limit: 1e3
539
714
  }, "group-task-data")).data?.buckets?.flatMap((b) => b.tasks ?? []) ?? [];
540
- if (params.query) {
541
- const keyword = params.query;
715
+ if (intent === "all_bugs") tasks = tasks.filter((task) => task.issueType?.uuid ? bugTypeUuids.includes(task.issueType.uuid) : false).filter((task) => isOpenOrInProgressBug(task)).sort((a, b) => getBugStatusPriority(a) - getBugStatusPriority(b));
716
+ if (intent === "all_tasks") tasks = tasks.filter((task) => task.issueType?.uuid ? taskTypeUuids.includes(task.issueType.uuid) : false);
717
+ if (assigneeUuid) tasks = tasks.filter((task) => task.assign?.uuid === assigneeUuid);
718
+ if (intent === "keyword" && params.query) {
719
+ const keyword = params.query.trim();
542
720
  const lower = keyword.toLowerCase();
543
721
  const numMatch = keyword.match(/^#?(\d+)$/);
544
722
  if (numMatch) tasks = tasks.filter((t) => t.number === Number.parseInt(numMatch[1], 10));
@@ -626,6 +804,108 @@ var OnesAdapter = class extends BaseAdapter {
626
804
  raw: task
627
805
  };
628
806
  }
807
+ async getTestcases(params) {
808
+ let libraryUuid = params.libraryUuid ?? this.config.options?.testcaseLibraryUuid;
809
+ if (!libraryUuid) {
810
+ const libs = (await this.graphql(TESTCASE_LIBRARY_LIST_QUERY, {}, "library-select")).data?.testcaseLibraries ?? [];
811
+ if (libs.length === 0) throw new Error("ONES: No testcase libraries found for this team");
812
+ libs.sort((a, b) => b.testcaseCaseCount - a.testcaseCaseCount);
813
+ libraryUuid = libs[0].uuid;
814
+ }
815
+ const task = ((await this.graphql(SEARCH_TASKS_QUERY, {
816
+ groupBy: { tasks: {} },
817
+ groupOrderBy: null,
818
+ orderBy: { createTime: "DESC" },
819
+ filterGroup: [{ number_in: [params.taskNumber] }],
820
+ search: null,
821
+ pagination: {
822
+ limit: 10,
823
+ preciseCount: false
824
+ },
825
+ limit: 10
826
+ }, "group-task-data")).data?.buckets?.flatMap((b) => b.tasks ?? []) ?? []).find((t) => t.number === params.taskNumber);
827
+ if (!task) throw new Error(`ONES: Task #${params.taskNumber} not found`);
828
+ const modules = (await this.graphql(TESTCASE_MODULE_SEARCH_QUERY, { filter: {
829
+ testcaseLibrary_in: [libraryUuid],
830
+ name_match: `#${params.taskNumber}`
831
+ } }, "find-testcase-module")).data?.testcaseModules ?? [];
832
+ if (modules.length === 0) throw new Error(`ONES: No testcase module matching "#${params.taskNumber}" in library ${libraryUuid}`);
833
+ const mod = modules[0];
834
+ const caseList = [];
835
+ let cursor = "";
836
+ let totalCount = 0;
837
+ while (true) {
838
+ const bucket = (await this.graphql(TESTCASE_LIST_PAGED_QUERY, {
839
+ testCaseFilter: [{
840
+ testcaseLibrary_in: [libraryUuid],
841
+ path_match: mod.uuid
842
+ }],
843
+ pagination: {
844
+ limit: 50,
845
+ after: cursor,
846
+ preciseCount: true
847
+ }
848
+ }, "testcase-list-paged")).data?.buckets?.[0];
849
+ if (!bucket) break;
850
+ caseList.push(...bucket.testcaseCases ?? []);
851
+ totalCount = bucket.pageInfo.totalCount;
852
+ if (!bucket.pageInfo.hasNextPage) break;
853
+ cursor = bucket.pageInfo.endCursor;
854
+ }
855
+ if (caseList.length === 0) return {
856
+ taskNumber: params.taskNumber,
857
+ taskName: task.name,
858
+ moduleName: mod.name,
859
+ moduleUuid: mod.uuid,
860
+ totalCount: 0,
861
+ cases: []
862
+ };
863
+ const allCases = [];
864
+ const BATCH_SIZE = 20;
865
+ for (let i = 0; i < caseList.length; i += BATCH_SIZE) {
866
+ const uuids = caseList.slice(i, i + BATCH_SIZE).map((c) => c.uuid);
867
+ const detailData = await this.graphql(TESTCASE_DETAIL_QUERY, {
868
+ testCaseFilter: { uuid_in: [...uuids, null] },
869
+ stepFilter: { testcaseCase_in: uuids }
870
+ }, "library-testcase-detail");
871
+ const cases = detailData.data?.testcaseCases ?? [];
872
+ const steps = detailData.data?.testcaseCaseSteps ?? [];
873
+ const stepsByCase = /* @__PURE__ */ new Map();
874
+ for (const step of steps) {
875
+ const caseUuid = step.testcaseCase.uuid;
876
+ if (!stepsByCase.has(caseUuid)) stepsByCase.set(caseUuid, []);
877
+ stepsByCase.get(caseUuid).push({
878
+ uuid: step.uuid,
879
+ index: step.index,
880
+ desc: step.desc ?? "",
881
+ result: step.result ?? ""
882
+ });
883
+ }
884
+ for (const c of cases) {
885
+ const freshDesc = c.desc ? await this.refreshImageUrls(c.desc) : "";
886
+ allCases.push({
887
+ uuid: c.uuid,
888
+ id: c.id,
889
+ name: c.name,
890
+ priority: c.priority?.value ?? "N/A",
891
+ type: c.type?.value ?? "Unknown",
892
+ assignName: c.assign?.name ?? null,
893
+ condition: c.condition ?? "",
894
+ desc: freshDesc,
895
+ steps: (stepsByCase.get(c.uuid) ?? []).sort((a, b) => a.index - b.index),
896
+ modulePath: c.path ?? ""
897
+ });
898
+ }
899
+ }
900
+ return {
901
+ taskNumber: params.taskNumber,
902
+ taskName: task.name,
903
+ moduleName: mod.name,
904
+ moduleUuid: mod.uuid,
905
+ totalCount,
906
+ cases: allCases
907
+ };
908
+ }
629
909
  };
630
910
 
631
911
  //#endregion
@@ -727,6 +1007,11 @@ function loadConfigFromEnv() {
727
1007
  const account = process.env.ONES_ACCOUNT;
728
1008
  const password = process.env.ONES_PASSWORD;
729
1009
  if (!apiBase || !account || !password) return null;
1010
+ let options;
1011
+ const configPath = findConfigFile(process.cwd());
1012
+ if (configPath) try {
1013
+ options = JSON.parse(readFileSync(configPath, "utf-8"))?.sources?.ones?.options;
1014
+ } catch {}
730
1015
  return {
731
1016
  sources: { ones: {
732
1017
  enabled: true,
@@ -735,7 +1020,8 @@ function loadConfigFromEnv() {
735
1020
  type: "ones-pkce",
736
1021
  emailEnv: "ONES_ACCOUNT",
737
1022
  passwordEnv: "ONES_PASSWORD"
738
- }
1023
+ },
1024
+ options
739
1025
  } },
740
1026
  defaultSource: "ones"
741
1027
  };
@@ -948,6 +1234,58 @@ function formatRequirement(req) {
948
1234
  return lines.join("\n");
949
1235
  }
950
1236
 
1237
+ //#endregion
1238
+ //#region src/tools/get-testcases.ts
1239
+ const GetTestcasesSchema = z.object({
1240
+ taskNumber: z.string().describe("Task number (e.g. \"302\" or \"#302\"). Finds all testcases in the matching module."),
1241
+ libraryUuid: z.string().optional().describe("Testcase library UUID. If omitted, uses configured default."),
1242
+ source: z.string().optional().describe("Source to fetch from. If omitted, uses the default source.")
1243
+ });
1244
+ async function handleGetTestcases(input, adapters, defaultSource) {
1245
+ const sourceType = input.source ?? defaultSource;
1246
+ if (!sourceType) throw new Error("No source specified and no default source configured");
1247
+ const adapter = adapters.get(sourceType);
1248
+ if (!adapter) throw new Error(`Source "${sourceType}" is not configured. Available: ${[...adapters.keys()].join(", ")}`);
1249
+ const numMatch = input.taskNumber.match(/^#?(\d+)$/);
1250
+ if (!numMatch) throw new Error(`Invalid task number: "${input.taskNumber}". Expected a number like "302" or "#302".`);
1251
+ return { content: [{
1252
+ type: "text",
1253
+ text: formatTestcases(await adapter.getTestcases({
1254
+ taskNumber: Number.parseInt(numMatch[1], 10),
1255
+ libraryUuid: input.libraryUuid
1256
+ }))
1257
+ }] };
1258
+ }
1259
+ function formatTestcases(result) {
1260
+ const lines = [
1261
+ `# ${result.taskName} — 测试用例`,
1262
+ "",
1263
+ `- **模块**: ${result.moduleName}`,
1264
+ `- **共 ${result.totalCount} 个用例**(已加载 ${result.cases.length} 个)`,
1265
+ ""
1266
+ ];
1267
+ for (const tc of result.cases) {
1268
+ lines.push(`## ${tc.id} ${tc.name}`);
1269
+ lines.push("");
1270
+ lines.push(`- 优先级: ${tc.priority} | 类型: ${tc.type}`);
1271
+ if (tc.assignName) lines.push(`- 维护人: ${tc.assignName}`);
1272
+ if (tc.condition) lines.push(`- 前置条件: ${tc.condition}`);
1273
+ if (tc.desc) lines.push(`- 备注: ${tc.desc}`);
1274
+ if (tc.steps.length > 0) {
1275
+ lines.push("");
1276
+ lines.push("| 步骤 | 操作描述 | 预期结果 |");
1277
+ lines.push("|------|----------|----------|");
1278
+ for (const step of tc.steps) {
1279
+ const desc = step.desc.replace(/\n/g, "<br>");
1280
+ const res = step.result.replace(/\n/g, "<br>");
1281
+ lines.push(`| ${step.index + 1} | ${desc} | ${res} |`);
1282
+ }
1283
+ }
1284
+ lines.push("");
1285
+ }
1286
+ return lines.join("\n");
1287
+ }
1288
+
951
1289
  //#endregion
952
1290
  //#region src/tools/list-sources.ts
953
1291
  async function handleListSources(adapters, config) {
@@ -982,6 +1320,9 @@ const SearchRequirementsSchema = z.object({
982
1320
  page: z.number().int().min(1).optional().describe("Page number (default: 1)"),
983
1321
  pageSize: z.number().int().min(1).max(50).optional().describe("Results per page (default: 20, max: 50)")
984
1322
  });
1323
+ function formatStatusMarker(status) {
1324
+ return `[${status.toUpperCase()}]`;
1325
+ }
985
1326
  async function handleSearchRequirements(input, adapters, defaultSource) {
986
1327
  const sourceType = input.source ?? defaultSource;
987
1328
  if (!sourceType) throw new Error("No source specified and no default source configured");
@@ -992,15 +1333,18 @@ async function handleSearchRequirements(input, adapters, defaultSource) {
992
1333
  page: input.page,
993
1334
  pageSize: input.pageSize
994
1335
  });
995
- const lines = [`Found **${result.total}** results (page ${result.page}/${Math.ceil(result.total / result.pageSize) || 1}):`, ""];
1336
+ const lines = [`Found **${result.total}** items (page ${result.page}/${Math.ceil(result.total / result.pageSize) || 1}):`, ""];
1337
+ if (/\u6211.*\u7F3A\u9677|bug|\u6211.*\u4EFB\u52A1/i.test(input.query)) {
1338
+ lines.push(`Query: ${input.query}`);
1339
+ lines.push("Use an item ID or number in the next step to fetch detail.");
1340
+ lines.push("");
1341
+ }
996
1342
  for (const item of result.items) {
997
- lines.push(`### ${item.id}: ${item.title}`);
1343
+ lines.push(`### ${formatStatusMarker(item.status)} ${item.id}: ${item.title}`);
998
1344
  lines.push(`- Status: ${item.status} | Priority: ${item.priority} | Type: ${item.type}`);
999
1345
  lines.push(`- Assignee: ${item.assignee ?? "Unassigned"}`);
1000
- if (item.description) {
1001
- const desc = item.description.length > 200 ? `${item.description.slice(0, 200)}...` : item.description;
1002
- lines.push(`- ${desc}`);
1003
- }
1346
+ const desc = item.description ? item.description.length > 200 ? `${item.description.slice(0, 200)}...` : item.description : "(empty)";
1347
+ lines.push(`- Content: ${desc}`);
1004
1348
  lines.push("");
1005
1349
  }
1006
1350
  return { content: [{
@@ -1121,6 +1465,19 @@ async function main() {
1121
1465
  };
1122
1466
  }
1123
1467
  });
1468
+ server.tool("get_testcases", "Get all test cases for a task by its number (e.g. 302). Searches the testcase library for a matching module and returns all cases with steps.", GetTestcasesSchema.shape, async (params) => {
1469
+ try {
1470
+ return await handleGetTestcases(params, adapters, config.config.defaultSource);
1471
+ } catch (err) {
1472
+ return {
1473
+ content: [{
1474
+ type: "text",
1475
+ text: `Error: ${err.message}`
1476
+ }],
1477
+ isError: true
1478
+ };
1479
+ }
1480
+ });
1124
1481
  const transport = new StdioServerTransport();
1125
1482
  await server.connect(transport);
1126
1483
  }