ai-dev-requirements 0.1.8 → 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.cjs CHANGED
@@ -202,6 +202,55 @@ const DEFAULT_STATUS_NOT_IN = [
202
202
  "Dn3k8ffK",
203
203
  "TbmY2So5"
204
204
  ];
205
+ const TESTCASE_LIBRARY_LIST_QUERY = `
206
+ query Q {
207
+ testcaseLibraries {
208
+ uuid name key
209
+ testcaseCaseCount
210
+ }
211
+ }
212
+ `;
213
+ const TESTCASE_MODULE_SEARCH_QUERY = `
214
+ query Q($filter: Filter) {
215
+ testcaseModules(filter: $filter) {
216
+ uuid name key
217
+ parent { uuid name }
218
+ }
219
+ }
220
+ `;
221
+ const TESTCASE_LIST_PAGED_QUERY = `
222
+ query PAGED_LIBRARY_TESTCASE_LIST($testCaseFilter: Filter, $pagination: Pagination) {
223
+ buckets(groupBy: {testcaseCases: {}}, pagination: $pagination) {
224
+ testcaseCases(filterGroup: $testCaseFilter, limit: 10000) {
225
+ uuid name key id
226
+ priority { uuid value }
227
+ type { uuid value }
228
+ assign { uuid name }
229
+ testcaseModule { uuid }
230
+ }
231
+ key
232
+ pageInfo { count totalCount hasNextPage endCursor }
233
+ }
234
+ }
235
+ `;
236
+ const TESTCASE_DETAIL_QUERY = `
237
+ query QUERY_TESTCASES_DETAIL($testCaseFilter: Filter, $stepFilter: Filter) {
238
+ testcaseCases(filter: $testCaseFilter) {
239
+ uuid name key id condition desc path
240
+ assign { uuid name }
241
+ priority { uuid value }
242
+ type { uuid value }
243
+ testcaseLibrary { uuid }
244
+ testcaseModule { uuid }
245
+ relatedTasks { uuid name number }
246
+ }
247
+ testcaseCaseSteps(filter: $stepFilter, orderBy: { index: ASC }) {
248
+ key uuid
249
+ testcaseCase { uuid }
250
+ desc result index
251
+ }
252
+ }
253
+ `;
205
254
  function parseOnesSearchIntent(query) {
206
255
  if (!query) return "keyword";
207
256
  const normalized = query.toLowerCase();
@@ -783,6 +832,108 @@ var OnesAdapter = class extends BaseAdapter {
783
832
  raw: task
784
833
  };
785
834
  }
835
+ async getTestcases(params) {
836
+ let libraryUuid = params.libraryUuid ?? this.config.options?.testcaseLibraryUuid;
837
+ if (!libraryUuid) {
838
+ const libs = (await this.graphql(TESTCASE_LIBRARY_LIST_QUERY, {}, "library-select")).data?.testcaseLibraries ?? [];
839
+ if (libs.length === 0) throw new Error("ONES: No testcase libraries found for this team");
840
+ libs.sort((a, b) => b.testcaseCaseCount - a.testcaseCaseCount);
841
+ libraryUuid = libs[0].uuid;
842
+ }
843
+ const task = ((await this.graphql(SEARCH_TASKS_QUERY, {
844
+ groupBy: { tasks: {} },
845
+ groupOrderBy: null,
846
+ orderBy: { createTime: "DESC" },
847
+ filterGroup: [{ number_in: [params.taskNumber] }],
848
+ search: null,
849
+ pagination: {
850
+ limit: 10,
851
+ preciseCount: false
852
+ },
853
+ limit: 10
854
+ }, "group-task-data")).data?.buckets?.flatMap((b) => b.tasks ?? []) ?? []).find((t) => t.number === params.taskNumber);
855
+ if (!task) throw new Error(`ONES: Task #${params.taskNumber} not found`);
856
+ const modules = (await this.graphql(TESTCASE_MODULE_SEARCH_QUERY, { filter: {
857
+ testcaseLibrary_in: [libraryUuid],
858
+ name_match: `#${params.taskNumber}`
859
+ } }, "find-testcase-module")).data?.testcaseModules ?? [];
860
+ if (modules.length === 0) throw new Error(`ONES: No testcase module matching "#${params.taskNumber}" in library ${libraryUuid}`);
861
+ const mod = modules[0];
862
+ const caseList = [];
863
+ let cursor = "";
864
+ let totalCount = 0;
865
+ while (true) {
866
+ const bucket = (await this.graphql(TESTCASE_LIST_PAGED_QUERY, {
867
+ testCaseFilter: [{
868
+ testcaseLibrary_in: [libraryUuid],
869
+ path_match: mod.uuid
870
+ }],
871
+ pagination: {
872
+ limit: 50,
873
+ after: cursor,
874
+ preciseCount: true
875
+ }
876
+ }, "testcase-list-paged")).data?.buckets?.[0];
877
+ if (!bucket) break;
878
+ caseList.push(...bucket.testcaseCases ?? []);
879
+ totalCount = bucket.pageInfo.totalCount;
880
+ if (!bucket.pageInfo.hasNextPage) break;
881
+ cursor = bucket.pageInfo.endCursor;
882
+ }
883
+ if (caseList.length === 0) return {
884
+ taskNumber: params.taskNumber,
885
+ taskName: task.name,
886
+ moduleName: mod.name,
887
+ moduleUuid: mod.uuid,
888
+ totalCount: 0,
889
+ cases: []
890
+ };
891
+ const allCases = [];
892
+ const BATCH_SIZE = 20;
893
+ for (let i = 0; i < caseList.length; i += BATCH_SIZE) {
894
+ const uuids = caseList.slice(i, i + BATCH_SIZE).map((c) => c.uuid);
895
+ const detailData = await this.graphql(TESTCASE_DETAIL_QUERY, {
896
+ testCaseFilter: { uuid_in: [...uuids, null] },
897
+ stepFilter: { testcaseCase_in: uuids }
898
+ }, "library-testcase-detail");
899
+ const cases = detailData.data?.testcaseCases ?? [];
900
+ const steps = detailData.data?.testcaseCaseSteps ?? [];
901
+ const stepsByCase = /* @__PURE__ */ new Map();
902
+ for (const step of steps) {
903
+ const caseUuid = step.testcaseCase.uuid;
904
+ if (!stepsByCase.has(caseUuid)) stepsByCase.set(caseUuid, []);
905
+ stepsByCase.get(caseUuid).push({
906
+ uuid: step.uuid,
907
+ index: step.index,
908
+ desc: step.desc ?? "",
909
+ result: step.result ?? ""
910
+ });
911
+ }
912
+ for (const c of cases) {
913
+ const freshDesc = c.desc ? await this.refreshImageUrls(c.desc) : "";
914
+ allCases.push({
915
+ uuid: c.uuid,
916
+ id: c.id,
917
+ name: c.name,
918
+ priority: c.priority?.value ?? "N/A",
919
+ type: c.type?.value ?? "Unknown",
920
+ assignName: c.assign?.name ?? null,
921
+ condition: c.condition ?? "",
922
+ desc: freshDesc,
923
+ steps: (stepsByCase.get(c.uuid) ?? []).sort((a, b) => a.index - b.index),
924
+ modulePath: c.path ?? ""
925
+ });
926
+ }
927
+ }
928
+ return {
929
+ taskNumber: params.taskNumber,
930
+ taskName: task.name,
931
+ moduleName: mod.name,
932
+ moduleUuid: mod.uuid,
933
+ totalCount,
934
+ cases: allCases
935
+ };
936
+ }
786
937
  };
787
938
 
788
939
  //#endregion
@@ -884,6 +1035,11 @@ function loadConfigFromEnv() {
884
1035
  const account = process.env.ONES_ACCOUNT;
885
1036
  const password = process.env.ONES_PASSWORD;
886
1037
  if (!apiBase || !account || !password) return null;
1038
+ let options;
1039
+ const configPath = findConfigFile(process.cwd());
1040
+ if (configPath) try {
1041
+ options = JSON.parse((0, node_fs.readFileSync)(configPath, "utf-8"))?.sources?.ones?.options;
1042
+ } catch {}
887
1043
  return {
888
1044
  sources: { ones: {
889
1045
  enabled: true,
@@ -892,7 +1048,8 @@ function loadConfigFromEnv() {
892
1048
  type: "ones-pkce",
893
1049
  emailEnv: "ONES_ACCOUNT",
894
1050
  passwordEnv: "ONES_PASSWORD"
895
- }
1051
+ },
1052
+ options
896
1053
  } },
897
1054
  defaultSource: "ones"
898
1055
  };
@@ -1105,6 +1262,58 @@ function formatRequirement(req) {
1105
1262
  return lines.join("\n");
1106
1263
  }
1107
1264
 
1265
+ //#endregion
1266
+ //#region src/tools/get-testcases.ts
1267
+ const GetTestcasesSchema = zod_v4.z.object({
1268
+ taskNumber: zod_v4.z.string().describe("Task number (e.g. \"302\" or \"#302\"). Finds all testcases in the matching module."),
1269
+ libraryUuid: zod_v4.z.string().optional().describe("Testcase library UUID. If omitted, uses configured default."),
1270
+ source: zod_v4.z.string().optional().describe("Source to fetch from. If omitted, uses the default source.")
1271
+ });
1272
+ async function handleGetTestcases(input, adapters, defaultSource) {
1273
+ const sourceType = input.source ?? defaultSource;
1274
+ if (!sourceType) throw new Error("No source specified and no default source configured");
1275
+ const adapter = adapters.get(sourceType);
1276
+ if (!adapter) throw new Error(`Source "${sourceType}" is not configured. Available: ${[...adapters.keys()].join(", ")}`);
1277
+ const numMatch = input.taskNumber.match(/^#?(\d+)$/);
1278
+ if (!numMatch) throw new Error(`Invalid task number: "${input.taskNumber}". Expected a number like "302" or "#302".`);
1279
+ return { content: [{
1280
+ type: "text",
1281
+ text: formatTestcases(await adapter.getTestcases({
1282
+ taskNumber: Number.parseInt(numMatch[1], 10),
1283
+ libraryUuid: input.libraryUuid
1284
+ }))
1285
+ }] };
1286
+ }
1287
+ function formatTestcases(result) {
1288
+ const lines = [
1289
+ `# ${result.taskName} — 测试用例`,
1290
+ "",
1291
+ `- **模块**: ${result.moduleName}`,
1292
+ `- **共 ${result.totalCount} 个用例**(已加载 ${result.cases.length} 个)`,
1293
+ ""
1294
+ ];
1295
+ for (const tc of result.cases) {
1296
+ lines.push(`## ${tc.id} ${tc.name}`);
1297
+ lines.push("");
1298
+ lines.push(`- 优先级: ${tc.priority} | 类型: ${tc.type}`);
1299
+ if (tc.assignName) lines.push(`- 维护人: ${tc.assignName}`);
1300
+ if (tc.condition) lines.push(`- 前置条件: ${tc.condition}`);
1301
+ if (tc.desc) lines.push(`- 备注: ${tc.desc}`);
1302
+ if (tc.steps.length > 0) {
1303
+ lines.push("");
1304
+ lines.push("| 步骤 | 操作描述 | 预期结果 |");
1305
+ lines.push("|------|----------|----------|");
1306
+ for (const step of tc.steps) {
1307
+ const desc = step.desc.replace(/\n/g, "<br>");
1308
+ const res = step.result.replace(/\n/g, "<br>");
1309
+ lines.push(`| ${step.index + 1} | ${desc} | ${res} |`);
1310
+ }
1311
+ }
1312
+ lines.push("");
1313
+ }
1314
+ return lines.join("\n");
1315
+ }
1316
+
1108
1317
  //#endregion
1109
1318
  //#region src/tools/list-sources.ts
1110
1319
  async function handleListSources(adapters, config) {
@@ -1284,6 +1493,19 @@ async function main() {
1284
1493
  };
1285
1494
  }
1286
1495
  });
1496
+ 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) => {
1497
+ try {
1498
+ return await handleGetTestcases(params, adapters, config.config.defaultSource);
1499
+ } catch (err) {
1500
+ return {
1501
+ content: [{
1502
+ type: "text",
1503
+ text: `Error: ${err.message}`
1504
+ }],
1505
+ isError: true
1506
+ };
1507
+ }
1508
+ });
1287
1509
  const transport = new _modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport();
1288
1510
  await server.connect(transport);
1289
1511
  }
package/dist/index.mjs CHANGED
@@ -174,6 +174,55 @@ const DEFAULT_STATUS_NOT_IN = [
174
174
  "Dn3k8ffK",
175
175
  "TbmY2So5"
176
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
+ `;
177
226
  function parseOnesSearchIntent(query) {
178
227
  if (!query) return "keyword";
179
228
  const normalized = query.toLowerCase();
@@ -755,6 +804,108 @@ var OnesAdapter = class extends BaseAdapter {
755
804
  raw: task
756
805
  };
757
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
+ }
758
909
  };
759
910
 
760
911
  //#endregion
@@ -856,6 +1007,11 @@ function loadConfigFromEnv() {
856
1007
  const account = process.env.ONES_ACCOUNT;
857
1008
  const password = process.env.ONES_PASSWORD;
858
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 {}
859
1015
  return {
860
1016
  sources: { ones: {
861
1017
  enabled: true,
@@ -864,7 +1020,8 @@ function loadConfigFromEnv() {
864
1020
  type: "ones-pkce",
865
1021
  emailEnv: "ONES_ACCOUNT",
866
1022
  passwordEnv: "ONES_PASSWORD"
867
- }
1023
+ },
1024
+ options
868
1025
  } },
869
1026
  defaultSource: "ones"
870
1027
  };
@@ -1077,6 +1234,58 @@ function formatRequirement(req) {
1077
1234
  return lines.join("\n");
1078
1235
  }
1079
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
+
1080
1289
  //#endregion
1081
1290
  //#region src/tools/list-sources.ts
1082
1291
  async function handleListSources(adapters, config) {
@@ -1256,6 +1465,19 @@ async function main() {
1256
1465
  };
1257
1466
  }
1258
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
+ });
1259
1481
  const transport = new StdioServerTransport();
1260
1482
  await server.connect(transport);
1261
1483
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/utils/map-status.ts","../src/adapters/base.ts","../src/adapters/ones.ts","../src/adapters/index.ts","../src/config/loader.ts","../src/tools/get-issue-detail.ts","../src/tools/get-related-issues.ts","../src/tools/get-requirement.ts","../src/tools/list-sources.ts","../src/tools/search-requirements.ts","../src/index.ts"],"sourcesContent":["import type { RequirementPriority, RequirementStatus, RequirementType } from '../types/requirement.js'\n\n// --- ONES status mapping ---\nconst ONES_STATUS_MAP: Record<string, RequirementStatus> = {\n to_do: 'open',\n in_progress: 'in_progress',\n done: 'done',\n closed: 'closed',\n}\n\n// --- Priority mappings ---\nconst ONES_PRIORITY_MAP: Record<string, RequirementPriority> = {\n urgent: 'critical',\n high: 'high',\n normal: 'medium',\n medium: 'medium',\n low: 'low',\n}\n\n// --- Type mappings ---\nconst ONES_TYPE_MAP: Record<string, RequirementType> = {\n demand: 'feature',\n 需求: 'feature',\n task: 'task',\n 任务: 'task',\n bug: 'bug',\n 缺陷: 'bug',\n story: 'story',\n 子任务: 'task',\n 工单: 'task',\n 测试任务: 'task',\n}\n\nexport function mapOnesStatus(status: string): RequirementStatus {\n return ONES_STATUS_MAP[status.toLowerCase()] ?? 'open'\n}\n\nexport function mapOnesPriority(priority: string): RequirementPriority {\n return ONES_PRIORITY_MAP[priority.toLowerCase()] ?? 'medium'\n}\n\nexport function mapOnesType(type: string): RequirementType {\n return ONES_TYPE_MAP[type.toLowerCase()] ?? 'task'\n}\n","import type { SourceConfig } from '../types/config.js'\nimport type { IssueDetail, RelatedIssue, Requirement, SearchResult, SourceType } from '../types/requirement.js'\n\nexport interface GetRequirementParams {\n id: string\n}\n\nexport interface SearchRequirementsParams {\n query: string\n page?: number\n pageSize?: number\n}\n\nexport interface GetRelatedIssuesParams {\n taskId: string\n}\n\nexport interface GetIssueDetailParams {\n issueId: string\n}\n\n/**\n * Abstract base class for source adapters.\n * Each adapter implements platform-specific logic for fetching requirements.\n */\nexport abstract class BaseAdapter {\n readonly sourceType: SourceType\n protected readonly config: SourceConfig\n protected readonly resolvedAuth: Record<string, string>\n\n constructor(\n sourceType: SourceType,\n config: SourceConfig,\n resolvedAuth: Record<string, string>,\n ) {\n this.sourceType = sourceType\n this.config = config\n this.resolvedAuth = resolvedAuth\n }\n\n /**\n * Fetch a single requirement by its ID.\n */\n abstract getRequirement(params: GetRequirementParams): Promise<Requirement>\n\n /**\n * Search requirements by query string.\n */\n abstract searchRequirements(params: SearchRequirementsParams): Promise<SearchResult>\n\n abstract getRelatedIssues(params: GetRelatedIssuesParams): Promise<RelatedIssue[]>\n\n abstract getIssueDetail(params: GetIssueDetailParams): Promise<IssueDetail>\n}\n","import type { SourceConfig } from '../types/config.js'\nimport type { IssueDetail, RelatedIssue, Requirement, SearchResult, SourceType } from '../types/requirement.js'\n\nimport type { GetIssueDetailParams, GetRelatedIssuesParams, GetRequirementParams, SearchRequirementsParams } from './base.js'\nimport crypto from 'node:crypto'\nimport { mapOnesPriority, mapOnesStatus, mapOnesType } from '../utils/map-status.js'\nimport { BaseAdapter } from './base.js'\n\n// ============ ONES GraphQL types ============\n\ninterface OnesTaskNode {\n key?: string\n uuid: string\n number: number\n name: string\n status: { uuid: string, name: string, category?: string }\n priority?: { value: string }\n issueType?: { uuid: string, name: string, detailType?: number }\n assign?: { uuid: string, name: string } | null\n owner?: { uuid: string, name: string } | null\n project?: { uuid: string, name: string }\n parent?: { uuid: string, number?: number, issueType?: { uuid: string, name: string } } | null\n relatedTasks?: OnesRelatedTask[]\n relatedWikiPages?: OnesWikiPage[]\n relatedWikiPagesCount?: number\n path?: string\n}\n\ninterface OnesWikiPage {\n uuid: string\n title: string\n referenceType?: number\n subReferenceType?: string\n errorMessage?: string\n}\n\ninterface OnesRelatedTask {\n uuid: string\n number: number\n name: string\n issueType: { uuid: string, name: string }\n status: { uuid: string, name: string, category?: string }\n assign?: { uuid: string, name: string } | null\n}\n\ninterface OnesIssueTypeNode {\n uuid: string\n name: string\n detailType: number\n}\n\ninterface OnesTeamUserNode {\n uuid?: string\n name?: string\n user?: {\n uuid?: string\n name?: string\n }\n org_user?: {\n org_user_uuid?: string\n name?: string\n }\n orgUser?: {\n uuid?: string\n name?: string\n }\n orgUserUuid?: string\n org_user_uuid?: string\n}\n\ninterface OnesTokenResponse {\n access_token: string\n token_type: string\n expires_in: number\n}\n\ninterface OnesLoginResponse {\n sid: string\n auth_user_uuid: string\n org_users: Array<{\n region_uuid: string\n org_uuid: string\n org_user: { org_user_uuid: string, name: string }\n org: { org_uuid: string, name: string }\n }>\n}\n\ninterface OnesSession {\n accessToken: string\n teamUuid: string\n orgUuid: string\n userUuid: string\n expiresAt: number\n}\n\n// ============ GraphQL queries ============\n\nconst TASK_DETAIL_QUERY = `\n query Task($key: Key) {\n task(key: $key) {\n key uuid number name\n issueType { uuid name }\n status { uuid name category }\n priority { value }\n assign { uuid name }\n owner { uuid name }\n project { uuid name }\n parent { uuid number issueType { uuid name } }\n relatedTasks {\n uuid number name\n issueType { uuid name }\n status { uuid name category }\n assign { uuid name }\n }\n relatedWikiPages {\n uuid\n title\n referenceType\n subReferenceType\n errorMessage\n }\n relatedWikiPagesCount\n }\n }\n`\n\nconst SEARCH_TASKS_QUERY = `\n query GROUP_TASK_DATA($groupBy: GroupBy, $groupOrderBy: OrderBy, $orderBy: OrderBy, $filterGroup: [Filter!], $search: Search, $pagination: Pagination, $limit: Int) {\n buckets(groupBy: $groupBy, orderBy: $groupOrderBy, pagination: $pagination, filter: $search) {\n key\n tasks(filterGroup: $filterGroup, orderBy: $orderBy, limit: $limit, includeAncestors: { pathField: \"path\" }) {\n key uuid number name\n issueType { uuid name detailType }\n status { uuid name category }\n priority { value }\n assign { uuid name }\n project { uuid name }\n }\n }\n }\n`\n\nconst ISSUE_TYPES_QUERY = `\n query IssueTypes($orderBy: OrderBy) {\n issueTypes(orderBy: $orderBy) {\n uuid\n name\n detailType\n }\n }\n`\n\n// Query to find a task by its number\nconst TASK_BY_NUMBER_QUERY = SEARCH_TASKS_QUERY\nconst RELATED_TASKS_QUERY = `\n query Task($key: Key) {\n task(key: $key) {\n key\n relatedTasks {\n key\n uuid\n name\n path\n deadline\n project { uuid name }\n priority { value }\n issueType {\n key uuid name detailType\n }\n subIssueType {\n key uuid name detailType\n }\n status {\n uuid name category\n }\n assign {\n uuid name\n }\n sprint {\n name uuid\n }\n statusCategory\n }\n }\n }\n`\n\nconst ISSUE_DETAIL_QUERY = `\n query Task($key: Key) {\n task(key: $key) {\n key uuid\n description\n descriptionText\n desc_rich: description\n name\n issueType { key uuid name detailType }\n subIssueType { key uuid name detailType }\n status { uuid name category }\n priority { value }\n assign { uuid name }\n owner { uuid name }\n solver { uuid name }\n project { uuid name }\n severityLevel { value }\n deadline(unit: ONESDATE)\n sprint { name uuid }\n }\n }\n`\n\nconst DEFAULT_STATUS_NOT_IN = ['FgMGkcaq', 'NvRwHBSo', 'Dn3k8ffK', 'TbmY2So5']\n\n// ============ Helpers ============\n\nfunction _getTaskStatusPriority(task: Pick<OnesTaskNode, 'status'>): number {\n const category = task.status?.category\n const name = task.status?.name\n\n if (category === 'to_do')\n return 0\n\n if (category === 'in_progress' && name === '修复中')\n return 1\n\n return Number.POSITIVE_INFINITY\n}\n\nfunction _isCommonTaskIssueType(task: Pick<OnesTaskNode, 'issueType'>): boolean {\n const detailType = task.issueType?.detailType\n\n if (detailType === 2 || detailType === 3)\n return true\n\n return task.issueType?.name === '任务' || task.issueType?.name === '缺陷'\n}\n\ntype OnesSearchIntent = 'all_bugs' | 'all_tasks' | 'keyword'\n\nfunction parseOnesSearchIntent(query: string): OnesSearchIntent {\n if (!query)\n return 'keyword'\n\n const normalized = query.toLowerCase()\n\n if (query.includes('\\u7F3A\\u9677') || normalized.includes('bug'))\n return 'all_bugs'\n\n if (query.includes('\\u4EFB\\u52A1'))\n return 'all_tasks'\n\n return 'keyword'\n}\n\nfunction extractAssigneeName(query: string, intent: OnesSearchIntent): string | null {\n if (intent === 'keyword')\n return null\n\n const trimmed = query.trim()\n if (!trimmed)\n return null\n\n const ownerStyleMatch = trimmed.match(/\\u8D1F\\u8D23\\u4EBA\\u4E3A(.+?)\\u7684?(?:\\u7F3A\\u9677|bug)$/i)\n if (ownerStyleMatch?.[1]) {\n return ownerStyleMatch[1].trim()\n }\n\n const genericMatch = trimmed.match(/^(查询)?(.+?)的(?:缺陷|bug|任务)$/i)\n const candidate = genericMatch?.[2]?.trim()\n if (!candidate || candidate.includes('我')) {\n return null\n }\n\n return candidate\n}\n\nfunction extractNamedAssignee(query: string, intent: OnesSearchIntent): string | null {\n if (intent === 'keyword')\n return null\n\n const compact = query.replace(/\\s+/g, '').trim()\n if (!compact)\n return null\n\n 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)\n if (ownerStyleMatch?.[1]) {\n return ownerStyleMatch[1].trim()\n }\n\n const genericMatch = compact.match(/^(?:\\u67E5\\u8BE2|\\u67E5\\u627E|\\u641C\\u7D22)?(.+?)\\u7684?(?:\\u7F3A\\u9677|bug|\\u4EFB\\u52A1)$/i)\n const candidate = genericMatch?.[1]?.trim()\n\n if (\n !candidate\n || candidate.startsWith('\\u6211')\n || /^(?:\\u6211|\\u6211\\u7684|\\u6211\\u6240\\u6709|\\u6211\\u5168\\u90E8|\\u672C\\u4EBA|\\u5F53\\u524D\\u7528\\u6237)$/.test(candidate)\n ) {\n return null\n }\n\n return candidate\n}\n\nfunction getBugStatusPriority(task: Pick<OnesTaskNode, 'status'>): number {\n if (task.status?.category === 'to_do')\n return 0\n\n if (task.status?.category === 'in_progress')\n return 1\n\n return Number.POSITIVE_INFINITY\n}\n\nfunction isOpenOrInProgressBug(task: Pick<OnesTaskNode, 'status'>): boolean {\n const category = task.status?.category\n return category === 'to_do' || category === 'in_progress'\n}\n\nfunction extractTeamUsers(payload: unknown): Array<{ uuid: string, name: string }> {\n const record = payload && typeof payload === 'object'\n ? payload as Record<string, unknown>\n : null\n\n if (!record)\n return []\n\n const candidates = [\n record.users,\n record.items,\n record.list,\n record.results,\n (record.data as Record<string, unknown> | undefined)?.users,\n (record.data as Record<string, unknown> | undefined)?.items,\n (record.data as Record<string, unknown> | undefined)?.list,\n (record.data as Record<string, unknown> | undefined)?.results,\n ]\n\n const rawUsers = candidates.find(Array.isArray)\n if (!rawUsers)\n return []\n\n return rawUsers\n .map((item) => {\n const user = item && typeof item === 'object'\n ? item as OnesTeamUserNode\n : null\n\n if (!user)\n return null\n\n const uuid = user.uuid\n ?? user.user?.uuid\n ?? user.orgUser?.uuid\n ?? user.orgUserUuid\n ?? user.org_user_uuid\n ?? user.org_user?.org_user_uuid\n\n const name = user.name\n ?? user.user?.name\n ?? user.orgUser?.name\n ?? user.org_user?.name\n\n if (!uuid || !name)\n return null\n\n return { uuid, name }\n })\n .filter((item): item is { uuid: string, name: string } => item !== null)\n}\n\nfunction base64Url(buffer: Buffer): string {\n return buffer.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\n}\n\nfunction getSetCookies(response: Response): string[] {\n const headers = response.headers as unknown as { getSetCookie?: () => string[] }\n if (headers.getSetCookie) {\n return headers.getSetCookie()\n }\n const raw = response.headers.get('set-cookie')\n return raw ? [raw] : []\n}\n\nfunction toRequirement(task: OnesTaskNode, description = ''): Requirement {\n return {\n id: task.uuid,\n source: 'ones',\n title: `#${task.number} ${task.name}`,\n description,\n status: mapOnesStatus(task.status?.category ?? 'to_do'),\n priority: mapOnesPriority(task.priority?.value ?? 'normal'),\n type: mapOnesType(task.issueType?.name ?? '任务'),\n labels: [],\n reporter: '',\n assignee: task.assign?.name ?? null,\n // ONES GraphQL does not return timestamps; these are fetch-time placeholders\n createdAt: '',\n updatedAt: '',\n dueDate: null,\n attachments: [],\n raw: task as unknown as Record<string, unknown>,\n }\n}\n\n// ============ ONES Adapter ============\n\nexport class OnesAdapter extends BaseAdapter {\n private session: OnesSession | null = null\n private issueTypesCache: OnesIssueTypeNode[] | null = null\n\n constructor(\n sourceType: SourceType,\n config: SourceConfig,\n resolvedAuth: Record<string, string>,\n ) {\n super(sourceType, config, resolvedAuth)\n }\n\n /**\n * ONES OAuth2 PKCE login flow.\n * Reference: D:\\company code\\ones\\packages\\core\\src\\auth.ts\n */\n private async login(): Promise<OnesSession> {\n if (this.session && Date.now() < this.session.expiresAt) {\n return this.session\n }\n\n const baseUrl = this.config.apiBase\n const email = this.resolvedAuth.email\n const password = this.resolvedAuth.password\n\n if (!email || !password) {\n throw new Error('ONES auth requires email and password (ones-pkce auth type)')\n }\n\n // 1. Get encryption certificate\n const certRes = await fetch(`${baseUrl}/identity/api/encryption_cert`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n })\n if (!certRes.ok) {\n throw new Error(`ONES: Failed to get encryption cert: ${certRes.status}`)\n }\n const cert = (await certRes.json()) as { public_key: string }\n\n // 2. Encrypt password with RSA public key\n const encrypted = crypto.publicEncrypt(\n { key: cert.public_key, padding: crypto.constants.RSA_PKCS1_PADDING },\n Buffer.from(password, 'utf-8'),\n )\n const encryptedPassword = encrypted.toString('base64')\n\n // 3. Login\n const loginRes = await fetch(`${baseUrl}/identity/api/login`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email, password: encryptedPassword }),\n })\n if (!loginRes.ok) {\n const text = await loginRes.text().catch(() => '')\n throw new Error(`ONES: Login failed: ${loginRes.status} ${text}`)\n }\n\n const cookies = getSetCookies(loginRes)\n .map(cookie => cookie.split(';')[0])\n .join('; ')\n const loginData = (await loginRes.json()) as OnesLoginResponse\n\n // Pick org user (first one, or match by config option)\n const orgUuid = this.config.options?.orgUuid as string | undefined\n let orgUser = loginData.org_users[0]\n if (orgUuid) {\n const match = loginData.org_users.find(u => u.org_uuid === orgUuid)\n if (match)\n orgUser = match\n }\n\n // 4. PKCE: generate code verifier + challenge\n const codeVerifier = base64Url(crypto.randomBytes(32))\n const codeChallenge = base64Url(\n crypto.createHash('sha256').update(codeVerifier).digest(),\n )\n\n // 5. Authorize\n const authorizeParams = new URLSearchParams({\n client_id: 'ones.v1',\n scope: `openid offline_access ones:org:${orgUser.region_uuid}:${orgUser.org_uuid}:${orgUser.org_user.org_user_uuid}`,\n response_type: 'code',\n code_challenge_method: 'S256',\n code_challenge: codeChallenge,\n redirect_uri: `${baseUrl}/auth/authorize/callback`,\n state: `org_uuid=${orgUser.org_uuid}`,\n })\n\n const authorizeRes = await fetch(`${baseUrl}/identity/authorize`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Cookie': cookies,\n },\n body: authorizeParams.toString(),\n redirect: 'manual',\n })\n\n const authorizeLocation = authorizeRes.headers.get('location')\n if (!authorizeLocation) {\n throw new Error('ONES: Authorize response missing location header')\n }\n const authRequestId = new URL(authorizeLocation).searchParams.get('id')\n if (!authRequestId) {\n throw new Error('ONES: Cannot parse auth_request_id from authorize redirect')\n }\n\n // 6. Finalize auth request\n const finalizeRes = await fetch(`${baseUrl}/identity/api/auth_request/finalize`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json;charset=UTF-8',\n 'Cookie': cookies,\n },\n body: JSON.stringify({\n auth_request_id: authRequestId,\n region_uuid: orgUser.region_uuid,\n org_uuid: orgUser.org_uuid,\n org_user_uuid: orgUser.org_user.org_user_uuid,\n }),\n })\n if (!finalizeRes.ok) {\n const text = await finalizeRes.text().catch(() => '')\n throw new Error(`ONES: Finalize failed: ${finalizeRes.status} ${text}`)\n }\n\n // 7. Callback to get authorization code\n const callbackRes = await fetch(\n `${baseUrl}/identity/authorize/callback?id=${authRequestId}&lang=zh`,\n {\n method: 'GET',\n headers: { Cookie: cookies },\n redirect: 'manual',\n },\n )\n\n const callbackLocation = callbackRes.headers.get('location')\n if (!callbackLocation) {\n throw new Error('ONES: Callback response missing location header')\n }\n const code = new URL(callbackLocation).searchParams.get('code')\n if (!code) {\n throw new Error('ONES: Cannot parse authorization code from callback redirect')\n }\n\n // 8. Exchange code for token\n const tokenRes = await fetch(`${baseUrl}/identity/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Cookie': cookies,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: 'ones.v1',\n code,\n code_verifier: codeVerifier,\n redirect_uri: `${baseUrl}/auth/authorize/callback`,\n }).toString(),\n })\n if (!tokenRes.ok) {\n const text = await tokenRes.text().catch(() => '')\n throw new Error(`ONES: Token exchange failed: ${tokenRes.status} ${text}`)\n }\n\n const token = (await tokenRes.json()) as OnesTokenResponse\n\n // 9. Get teams to find teamUuid\n const teamsRes = await fetch(\n `${baseUrl}/project/api/project/organization/${orgUser.org_uuid}/stamps/data?t=org_my_team`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${token.access_token}`,\n 'Content-Type': 'application/json;charset=UTF-8',\n },\n body: JSON.stringify({ org_my_team: 0 }),\n },\n )\n if (!teamsRes.ok) {\n throw new Error(`ONES: Failed to fetch teams: ${teamsRes.status}`)\n }\n\n const teamsData = (await teamsRes.json()) as {\n org_my_team?: { teams?: Array<{ uuid: string, name: string }> }\n }\n const teams = teamsData.org_my_team?.teams ?? []\n\n // Pick team by config option or default to first\n const configTeamUuid = this.config.options?.teamUuid as string | undefined\n let teamUuid = teams[0]?.uuid\n if (configTeamUuid) {\n const match = teams.find(t => t.uuid === configTeamUuid)\n if (match)\n teamUuid = match.uuid\n }\n\n if (!teamUuid) {\n throw new Error('ONES: No teams found for this user')\n }\n\n this.session = {\n accessToken: token.access_token,\n teamUuid,\n orgUuid: orgUser.org_uuid,\n userUuid: orgUser.org_user.org_user_uuid,\n expiresAt: Date.now() + (token.expires_in - 60) * 1000, // refresh 60s early\n }\n\n return this.session\n }\n\n /**\n * Execute a GraphQL query against ONES project API.\n */\n private async graphql<T>(query: string, variables: Record<string, unknown>, tag?: string): Promise<T> {\n const session = await this.login()\n const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/items/graphql${tag ? `?t=${encodeURIComponent(tag)}` : ''}`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${session.accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ query, variables }),\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`ONES GraphQL error: ${response.status} ${text}`)\n }\n\n return response.json() as Promise<T>\n }\n\n private async fetchIssueTypes(): Promise<OnesIssueTypeNode[]> {\n if (this.issueTypesCache)\n return this.issueTypesCache\n\n const data = await this.graphql<{ data?: { issueTypes?: OnesIssueTypeNode[] } }>(\n ISSUE_TYPES_QUERY,\n { orderBy: { namePinyin: 'ASC' } },\n 'issueTypes',\n )\n\n this.issueTypesCache = data.data?.issueTypes ?? []\n return this.issueTypesCache\n }\n\n private async searchTeamUsers(keyword: string): Promise<Array<{ uuid: string, name: string }>> {\n const session = await this.login()\n const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/users/search`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${session.accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n keyword,\n status: [1],\n team_member_status: [1, 4],\n types: [1, 10],\n }),\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`ONES user search error: ${response.status} ${text}`)\n }\n\n return extractTeamUsers(await response.json())\n }\n\n private async resolveAssigneeUuid(name: string): Promise<string | null> {\n const trimmed = name.trim()\n if (!trimmed)\n return null\n\n const users = await this.searchTeamUsers(trimmed)\n const exactMatch = users.find(user => user.name === trimmed)\n if (exactMatch)\n return exactMatch.uuid\n\n const normalizedTarget = trimmed.toLowerCase()\n const fuzzyMatch = users.find(user => user.name.toLowerCase().includes(normalizedTarget))\n return fuzzyMatch?.uuid ?? null\n }\n\n /**\n * Fetch task info via REST API (includes description/rich fields not available in GraphQL).\n * Reference: ones/packages/core/src/tasks.ts → fetchTaskInfo\n */\n private async fetchTaskInfo(taskUuid: string): Promise<Record<string, unknown>> {\n const session = await this.login()\n const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/task/${taskUuid}/info`\n\n const response = await fetch(url, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n })\n\n if (!response.ok) {\n return {}\n }\n\n return response.json() as Promise<Record<string, unknown>>\n }\n\n /**\n * Resolve a fresh signed URL for an attachment resource via ONES attachment API.\n * Endpoint: /project/api/project/team/{teamUuid}/res/attachment/{resourceUuid}\n * Returns a redirect URL with a fresh OSS signature.\n */\n private async getAttachmentUrl(resourceUuid: string): Promise<string | null> {\n const session = await this.login()\n const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/res/attachment/${resourceUuid}?op=${encodeURIComponent('imageMogr2/auto-orient')}`\n\n try {\n // First try with redirect: 'manual' to capture 302 Location header\n const manualRes = await fetch(url, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n redirect: 'manual',\n })\n\n if (manualRes.status === 302 || manualRes.status === 301) {\n const location = manualRes.headers.get('location')\n if (location)\n return location\n }\n\n // Fallback: follow redirects and use the final URL\n const followRes = await fetch(url, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n redirect: 'follow',\n })\n\n // If redirected, response.url will be the final signed URL\n if (followRes.url && followRes.url !== url) {\n return followRes.url\n }\n\n if (followRes.ok) {\n const text = await followRes.text()\n if (text.startsWith('http')) {\n return text.trim()\n }\n try {\n const data = JSON.parse(text) as { url?: string }\n return data.url ?? null\n }\n catch {\n return null\n }\n }\n\n console.error(`[getAttachmentUrl] Failed for resource ${resourceUuid}: status ${followRes.status}`)\n return null\n }\n catch (err) {\n console.error(`[getAttachmentUrl] Error for resource ${resourceUuid}:`, err)\n return null\n }\n }\n\n /**\n * Replace stale image URLs in HTML with fresh signed URLs from the attachment API.\n * Extracts data-uuid from <img> tags and resolves fresh URLs in parallel.\n */\n private async refreshImageUrls(html: string): Promise<string> {\n if (!html)\n return html\n\n // Extract all img tags with data-uuid\n const imgRegex = /<img\\s[^>]*data-uuid=\"([^\"]+)\"[^>]*>/g\n const matches = Array.from(html.matchAll(imgRegex))\n\n if (matches.length === 0)\n return html\n\n // Resolve fresh URLs in parallel\n const replacements = await Promise.all(\n matches.map(async (match) => {\n const dataUuid = match[1]\n const freshUrl = await this.getAttachmentUrl(dataUuid)\n return { fullMatch: match[0], dataUuid, freshUrl }\n }),\n )\n\n let result = html\n for (const { fullMatch, freshUrl } of replacements) {\n if (freshUrl) {\n // Replace the src attribute in the img tag with the fresh URL\n const updatedImg = fullMatch.replace(/src=\"[^\"]*\"/, `src=\"${freshUrl}\"`)\n result = result.replace(fullMatch, updatedImg)\n }\n }\n\n return result\n }\n\n /**\n * Fetch wiki page content via REST API.\n * Endpoint: /wiki/api/wiki/team/{teamUuid}/online_page/{wikiUuid}/content\n */\n private async fetchWikiContent(wikiUuid: string): Promise<string> {\n const session = await this.login()\n const url = `${this.config.apiBase}/wiki/api/wiki/team/${session.teamUuid}/online_page/${wikiUuid}/content`\n\n const response = await fetch(url, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n })\n\n if (!response.ok) {\n return ''\n }\n\n const data = await response.json() as { content?: string }\n return data.content ?? ''\n }\n\n /**\n * Fetch a single task by UUID or number (e.g. \"#95945\" or \"95945\").\n * If a number is given, searches first to resolve the UUID.\n */\n async getRequirement(params: GetRequirementParams): Promise<Requirement> {\n let taskUuid = params.id\n\n // If the ID looks like a number (with or without #), search to find the UUID\n const numMatch = taskUuid.match(/^#?(\\d+)$/)\n if (numMatch) {\n const taskNumber = Number.parseInt(numMatch[1], 10)\n const searchData = await this.graphql<{\n data?: { buckets?: Array<{ tasks?: OnesTaskNode[] }> }\n }>(\n TASK_BY_NUMBER_QUERY,\n {\n groupBy: { tasks: {} },\n groupOrderBy: null,\n orderBy: { createTime: 'DESC' },\n filterGroup: [{ number_in: [taskNumber] }],\n search: null,\n pagination: { limit: 10, preciseCount: false },\n limit: 10,\n },\n 'group-task-data',\n )\n\n const allTasks = searchData.data?.buckets?.flatMap(b => b.tasks ?? []) ?? []\n const found = allTasks.find(t => t.number === taskNumber)\n\n if (!found) {\n throw new Error(`ONES: Task #${taskNumber} not found in current team`)\n }\n taskUuid = found.uuid\n }\n\n // Fetch GraphQL data (structure, relations, wiki pages)\n const graphqlData = await this.graphql<{ data?: { task?: OnesTaskNode } }>(\n TASK_DETAIL_QUERY,\n { key: `task-${taskUuid}` },\n 'Task',\n )\n\n const task = graphqlData.data?.task\n if (!task) {\n throw new Error(`ONES: Task \"${taskUuid}\" not found`)\n }\n\n // Fetch wiki page content for related requirement docs (in parallel)\n const wikiPages = task.relatedWikiPages ?? []\n const wikiContents = await Promise.all(\n wikiPages\n .filter(w => !w.errorMessage)\n .map(async (wiki) => {\n const content = await this.fetchWikiContent(wiki.uuid)\n return { title: wiki.title, uuid: wiki.uuid, content }\n }),\n )\n\n // Build description: task info + wiki requirement docs\n const parts: string[] = []\n\n // Task basic info\n parts.push(`# #${task.number} ${task.name}`)\n parts.push('')\n parts.push(`- **Type**: ${task.issueType?.name ?? 'Unknown'}`)\n parts.push(`- **Status**: ${task.status?.name ?? 'Unknown'}`)\n parts.push(`- **Assignee**: ${task.assign?.name ?? 'Unassigned'}`)\n if (task.owner?.name) {\n parts.push(`- **Owner**: ${task.owner.name}`)\n }\n if (task.project?.name) {\n parts.push(`- **Project**: ${task.project.name}`)\n }\n parts.push(`- **UUID**: ${task.uuid}`)\n\n // Related tasks\n if (task.relatedTasks?.length) {\n parts.push('')\n parts.push('## Related Tasks')\n for (const related of task.relatedTasks) {\n const assignee = related.assign?.name ?? 'Unassigned'\n parts.push(`- #${related.number} ${related.name} [${related.issueType?.name}] (${related.status?.name}) — ${assignee}`)\n }\n }\n\n // Parent task\n if (task.parent?.uuid) {\n parts.push('')\n parts.push('## Parent Task')\n parts.push(`- UUID: ${task.parent.uuid}`)\n if (task.parent.number) {\n parts.push(`- Number: #${task.parent.number}`)\n }\n }\n\n // Wiki requirement documents (the core requirement content)\n if (wikiContents.length > 0) {\n parts.push('')\n parts.push('---')\n parts.push('')\n parts.push('## Requirement Documents')\n for (const wiki of wikiContents) {\n parts.push('')\n parts.push(`### ${wiki.title}`)\n parts.push('')\n if (wiki.content) {\n parts.push(wiki.content)\n }\n else {\n parts.push('(No content available)')\n }\n }\n }\n\n const req = toRequirement(task, parts.join('\\n'))\n\n return req\n }\n\n /**\n * Search tasks assigned to current user via GraphQL.\n * Uses keyword-based local filtering (matching ONES reference implementation).\n */\n async searchRequirements(params: SearchRequirementsParams): Promise<SearchResult> {\n const page = params.page ?? 1\n const pageSize = params.pageSize ?? 50\n const intent = parseOnesSearchIntent(params.query)\n const assigneeName = extractNamedAssignee(params.query, intent) ?? extractAssigneeName(params.query, intent)\n const assigneeUuid = assigneeName\n ? await this.resolveAssigneeUuid(assigneeName)\n : null\n\n if (assigneeName && !assigneeUuid) {\n return {\n items: [],\n total: 0,\n page,\n pageSize,\n }\n }\n\n let bugTypeUuids: string[] = []\n let taskTypeUuids: string[] = []\n\n if (intent === 'all_bugs' || intent === 'all_tasks') {\n const issueTypes = await this.fetchIssueTypes()\n bugTypeUuids = issueTypes.filter(item => item.detailType === 3).map(item => item.uuid)\n taskTypeUuids = issueTypes.filter(item => item.detailType === 2).map(item => item.uuid)\n }\n\n const filter: Record<string, unknown> = {\n status_notIn: DEFAULT_STATUS_NOT_IN,\n }\n\n if (assigneeName) {\n filter.assign_in = [assigneeUuid]\n }\n else {\n filter.assign_in = ['${currentUser}']\n }\n\n if (intent === 'all_bugs')\n filter.issueType_in = bugTypeUuids\n\n if (intent === 'all_tasks')\n filter.issueType_in = taskTypeUuids\n\n const data = await this.graphql<{\n data?: {\n buckets?: Array<{\n key: string\n tasks?: OnesTaskNode[]\n }>\n }\n }>(\n SEARCH_TASKS_QUERY,\n {\n groupBy: { tasks: {} },\n groupOrderBy: null,\n orderBy: { position: 'ASC', createTime: 'DESC' },\n filterGroup: [filter],\n search: null,\n pagination: { limit: pageSize * page, preciseCount: false },\n limit: 1000,\n },\n 'group-task-data',\n )\n\n let tasks = data.data?.buckets?.flatMap(b => b.tasks ?? []) ?? []\n\n if (intent === 'all_bugs') {\n tasks = tasks\n .filter(task => task.issueType?.uuid ? bugTypeUuids.includes(task.issueType.uuid) : false)\n .filter(task => isOpenOrInProgressBug(task))\n .sort((a, b) => getBugStatusPriority(a) - getBugStatusPriority(b))\n }\n\n if (intent === 'all_tasks') {\n // detailType = 1 的需求不属于“我的任务”列表入口。\n // 需求详情查询继续走 getRequirement(id/number),因为需求通常由产品负责人维护,\n // 当前登录用户不一定能通过 assign_in: ['${currentUser}'] 查到需求项。\n tasks = tasks.filter(task => task.issueType?.uuid ? taskTypeUuids.includes(task.issueType.uuid) : false)\n }\n\n if (assigneeUuid) {\n tasks = tasks.filter(task => task.assign?.uuid === assigneeUuid)\n }\n\n // Local keyword filter (matching ones-api.ts behavior)\n if (intent === 'keyword' && params.query) {\n const keyword = params.query.trim()\n const lower = keyword.toLowerCase()\n const numMatch = keyword.match(/^#?(\\d+)$/)\n\n if (numMatch) {\n tasks = tasks.filter(t => t.number === Number.parseInt(numMatch[1], 10))\n }\n else {\n tasks = tasks.filter(t => t.name.toLowerCase().includes(lower))\n }\n }\n\n // Paginate locally\n const total = tasks.length\n const start = (page - 1) * pageSize\n const paged = tasks.slice(start, start + pageSize)\n\n return {\n items: paged.map(t => toRequirement(t)),\n total,\n page,\n pageSize,\n }\n }\n\n async getRelatedIssues(params: GetRelatedIssuesParams): Promise<RelatedIssue[]> {\n const session = await this.login()\n\n const taskKey = params.taskId.startsWith('task-')\n ? params.taskId\n : `task-${params.taskId}`\n\n const data = await this.graphql<{\n data?: {\n task?: {\n key: string\n relatedTasks: Array<{\n key: string\n uuid: string\n name: string\n issueType: { key: string, uuid: string, name: string, detailType: number }\n subIssueType?: { key: string, uuid: string, name: string, detailType: number } | null\n status: { uuid: string, name: string, category: string }\n assign?: { uuid: string, name: string } | null\n priority?: { value: string } | null\n project?: { uuid: string, name: string } | null\n }>\n }\n }\n }>(RELATED_TASKS_QUERY, { key: taskKey }, 'Task')\n\n const relatedTasks = data.data?.task?.relatedTasks ?? []\n\n // Filter: detailType === 3 (defect) + status.category === \"to_do\" (pending)\n // Returns ALL pending defects, not just current user's\n const filtered = relatedTasks.filter((t) => {\n const isDefect = t.issueType?.detailType === 3\n || t.subIssueType?.detailType === 3\n const isTodo = t.status?.category === 'to_do'\n return isDefect && isTodo\n })\n\n // Sort: current user's defects first\n const currentUserUuid = session.userUuid\n filtered.sort((a, b) => {\n const aIsCurrent = a.assign?.uuid === currentUserUuid ? 0 : 1\n const bIsCurrent = b.assign?.uuid === currentUserUuid ? 0 : 1\n return aIsCurrent - bIsCurrent\n })\n\n return filtered.map(t => ({\n key: t.key,\n uuid: t.uuid,\n name: t.name,\n issueTypeName: t.issueType?.name ?? 'Unknown',\n statusName: t.status?.name ?? 'Unknown',\n statusCategory: t.status?.category ?? 'unknown',\n assignName: t.assign?.name ?? null,\n assignUuid: t.assign?.uuid ?? null,\n priorityValue: t.priority?.value ?? null,\n projectName: t.project?.name ?? null,\n }))\n }\n\n async getIssueDetail(params: GetIssueDetailParams): Promise<IssueDetail> {\n let issueKey: string\n\n // Support number lookup (e.g. \"98086\" or \"#98086\")\n const numMatch = params.issueId.match(/^#?(\\d+)$/)\n if (numMatch) {\n const taskNumber = Number.parseInt(numMatch[1], 10)\n const searchData = await this.graphql<{\n data?: { buckets?: Array<{ tasks?: Array<{ uuid: string, number: number }> }> }\n }>(\n SEARCH_TASKS_QUERY,\n {\n groupBy: { tasks: {} },\n groupOrderBy: null,\n orderBy: { createTime: 'DESC' },\n filterGroup: [{ number_in: [taskNumber] }],\n search: null,\n pagination: { limit: 10, preciseCount: false },\n limit: 10,\n },\n 'group-task-data',\n )\n\n const allTasks = searchData.data?.buckets?.flatMap(b => b.tasks ?? []) ?? []\n const found = allTasks.find(t => t.number === taskNumber)\n if (!found) {\n throw new Error(`ONES: Issue #${taskNumber} not found in current team`)\n }\n issueKey = `task-${found.uuid}`\n }\n else {\n issueKey = params.issueId.startsWith('task-')\n ? params.issueId\n : `task-${params.issueId}`\n }\n\n const data = await this.graphql<{\n data?: {\n task?: {\n key: string\n uuid: string\n name: string\n description: string\n descriptionText: string\n desc_rich: string\n issueType: { name: string }\n status: { name: string, category: string }\n priority?: { value: string } | null\n assign?: { uuid: string, name: string } | null\n owner?: { uuid: string, name: string } | null\n solver?: { uuid: string, name: string } | null\n project?: { uuid: string, name: string } | null\n severityLevel?: { value: string } | null\n deadline?: string | null\n sprint?: { name: string } | null\n }\n }\n }>(ISSUE_DETAIL_QUERY, { key: issueKey }, 'Task')\n\n const task = data.data?.task\n if (!task) {\n throw new Error(`ONES: Issue \"${issueKey}\" not found`)\n }\n\n // Fetch fresh description via REST API\n const taskInfo = await this.fetchTaskInfo(task.uuid)\n const rawDescription = (taskInfo.desc as string) ?? task.description ?? ''\n const rawDescRich = (taskInfo.desc_rich as string) ?? task.desc_rich ?? ''\n\n // Refresh image URLs via attachment API (signed URLs expire after 1 hour)\n const freshDescription = await this.refreshImageUrls(rawDescription)\n const freshDescRich = await this.refreshImageUrls(rawDescRich)\n\n return {\n key: task.key,\n uuid: task.uuid,\n name: task.name,\n description: freshDescription,\n descriptionRich: freshDescRich,\n descriptionText: task.descriptionText ?? '',\n issueTypeName: task.issueType?.name ?? 'Unknown',\n statusName: task.status?.name ?? 'Unknown',\n statusCategory: task.status?.category ?? 'unknown',\n assignName: task.assign?.name ?? null,\n ownerName: task.owner?.name ?? null,\n solverName: task.solver?.name ?? null,\n priorityValue: task.priority?.value ?? null,\n severityLevel: task.severityLevel?.value ?? null,\n projectName: task.project?.name ?? null,\n deadline: task.deadline ?? null,\n sprintName: task.sprint?.name ?? null,\n raw: task as unknown as Record<string, unknown>,\n }\n }\n}\n","import type { SourceConfig } from '../types/config.js'\nimport type { SourceType } from '../types/requirement.js'\nimport type { BaseAdapter } from './base.js'\nimport { OnesAdapter } from './ones.js'\n\nconst ADAPTER_MAP: Record<string, new (\n sourceType: SourceType,\n config: SourceConfig,\n resolvedAuth: Record<string, string>,\n) => BaseAdapter> = {\n ones: OnesAdapter,\n}\n\n/**\n * Factory function to create the appropriate adapter based on source type.\n */\nexport function createAdapter(\n sourceType: SourceType,\n config: SourceConfig,\n resolvedAuth: Record<string, string>,\n): BaseAdapter {\n const AdapterClass = ADAPTER_MAP[sourceType]\n if (!AdapterClass) {\n throw new Error(\n `Unsupported source type: \"${sourceType}\". Supported: ${Object.keys(ADAPTER_MAP).join(', ')}`,\n )\n }\n return new AdapterClass(sourceType, config, resolvedAuth)\n}\n\nexport { BaseAdapter } from './base.js'\nexport { OnesAdapter } from './ones.js'\n","import type { AuthConfig } from '../types/auth.js'\nimport type { McpConfig, SourceConfig } from '../types/config.js'\nimport type { SourceType } from '../types/requirement.js'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { z } from 'zod/v4'\n\nconst AuthSchema = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('token'),\n tokenEnv: z.string(),\n }),\n z.object({\n type: z.literal('basic'),\n usernameEnv: z.string(),\n passwordEnv: z.string(),\n }),\n z.object({\n type: z.literal('oauth2'),\n clientIdEnv: z.string(),\n clientSecretEnv: z.string(),\n tokenUrl: z.string().url(),\n }),\n z.object({\n type: z.literal('cookie'),\n cookieEnv: z.string(),\n }),\n z.object({\n type: z.literal('custom'),\n headerName: z.string(),\n valueEnv: z.string(),\n }),\n z.object({\n type: z.literal('ones-pkce'),\n emailEnv: z.string(),\n passwordEnv: z.string(),\n }),\n])\n\nconst SourceConfigSchema = z.object({\n enabled: z.boolean(),\n apiBase: z.string().url(),\n auth: AuthSchema,\n headers: z.record(z.string(), z.string()).optional(),\n options: z.record(z.string(), z.unknown()).optional(),\n})\n\nconst SourcesSchema = z.object({\n ones: SourceConfigSchema.optional(),\n})\n\nconst McpConfigSchema = z.object({\n sources: SourcesSchema,\n defaultSource: z.enum(['ones']).optional(),\n})\n\nconst CONFIG_FILENAME = '.requirements-mcp.json'\n\n/**\n * Search for config file starting from `startDir` and walking up to the root.\n */\nfunction findConfigFile(startDir: string): string | null {\n let dir = resolve(startDir)\n while (true) {\n const candidate = resolve(dir, CONFIG_FILENAME)\n if (existsSync(candidate)) {\n return candidate\n }\n const parent = dirname(dir)\n if (parent === dir)\n break\n dir = parent\n }\n return null\n}\n\n/**\n * Resolve environment variable references in auth config.\n * Reads actual env var values for fields ending with \"Env\".\n */\nfunction resolveAuthEnv(auth: AuthConfig): Record<string, string> {\n const resolved: Record<string, string> = {}\n\n for (const [key, value] of Object.entries(auth)) {\n if (key === 'type')\n continue\n if (key.endsWith('Env') && typeof value === 'string') {\n const envValue = process.env[value]\n if (!envValue) {\n throw new Error(`Environment variable \"${value}\" is not set (required by auth.${key})`)\n }\n // Strip the \"Env\" suffix for the resolved key\n const resolvedKey = key.slice(0, -3)\n resolved[resolvedKey] = envValue\n }\n else if (typeof value === 'string') {\n resolved[key] = value\n }\n }\n\n return resolved\n}\n\nexport interface ResolvedSource {\n type: SourceType\n config: SourceConfig\n resolvedAuth: Record<string, string>\n}\n\nexport interface LoadConfigResult {\n config: McpConfig\n sources: ResolvedSource[]\n configPath: string\n}\n\n/**\n * Try to build config purely from environment variables.\n * Required env vars: ONES_API_BASE, ONES_ACCOUNT, ONES_PASSWORD\n * Returns null if the required env vars are not all present.\n */\nfunction loadConfigFromEnv(): McpConfig | null {\n const apiBase = process.env.ONES_API_BASE\n const account = process.env.ONES_ACCOUNT\n const password = process.env.ONES_PASSWORD\n\n if (!apiBase || !account || !password) {\n return null\n }\n\n return {\n sources: {\n ones: {\n enabled: true,\n apiBase,\n auth: {\n type: 'ones-pkce',\n emailEnv: 'ONES_ACCOUNT',\n passwordEnv: 'ONES_PASSWORD',\n },\n },\n },\n defaultSource: 'ones',\n }\n}\n\n/**\n * Load and validate the MCP config.\n * Priority: env vars (ONES_API_BASE + ONES_ACCOUNT + ONES_PASSWORD) > config file (.requirements-mcp.json).\n * Searches from `startDir` (defaults to cwd) upward for the file.\n */\nexport function loadConfig(startDir?: string): LoadConfigResult {\n // 1. Try environment variables first (simplest setup for MCP)\n const envConfig = loadConfigFromEnv()\n if (envConfig) {\n const sources: ResolvedSource[] = []\n for (const [type, sourceConfig] of Object.entries(envConfig.sources)) {\n if (sourceConfig && sourceConfig.enabled) {\n const resolvedAuth = resolveAuthEnv(sourceConfig.auth)\n sources.push({\n type: type as SourceType,\n config: sourceConfig,\n resolvedAuth,\n })\n }\n }\n return { config: envConfig, sources, configPath: 'env' }\n }\n\n // 2. Fall back to config file\n const dir = startDir ?? process.cwd()\n const configPath = findConfigFile(dir)\n\n if (!configPath) {\n throw new Error(\n `Config not found. Either set env vars (ONES_API_BASE, ONES_ACCOUNT, ONES_PASSWORD) `\n + `or create \"${CONFIG_FILENAME}\" based on .requirements-mcp.json.example`,\n )\n }\n\n const raw = readFileSync(configPath, 'utf-8')\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n }\n catch {\n throw new Error(`Invalid JSON in ${configPath}`)\n }\n\n const result = McpConfigSchema.safeParse(parsed)\n if (!result.success) {\n throw new Error(\n `Invalid config in ${configPath}:\\n${result.error.issues.map(i => ` - ${i.path.join('.')}: ${i.message}`).join('\\n')}`,\n )\n }\n\n const config = result.data as McpConfig\n\n // Resolve enabled sources\n const sources: ResolvedSource[] = []\n for (const [type, sourceConfig] of Object.entries(config.sources)) {\n if (sourceConfig && sourceConfig.enabled) {\n const resolvedAuth = resolveAuthEnv(sourceConfig.auth)\n sources.push({\n type: type as SourceType,\n config: sourceConfig,\n resolvedAuth,\n })\n }\n }\n\n if (sources.length === 0) {\n throw new Error('No enabled sources found in config. Enable at least one source.')\n }\n\n return { config, sources, configPath }\n}\n\nexport { findConfigFile, loadConfigFromEnv, resolveAuthEnv }\n","import type { BaseAdapter } from '../adapters/base.js'\nimport type { IssueDetail } from '../types/requirement.js'\nimport { z } from 'zod/v4'\n\nexport const GetIssueDetailSchema = z.object({\n issueId: z.string().describe('The issue task ID or key (e.g. \"6W9vW3y8J9DO66Pu\" or \"task-6W9vW3y8J9DO66Pu\")'),\n source: z.string().optional().describe('Source to fetch from. If omitted, uses the default source.'),\n})\n\nexport type GetIssueDetailInput = z.infer<typeof GetIssueDetailSchema>\n\n/**\n * Download an image from URL and return as base64 data URI.\n * Returns null if download fails.\n */\nasync function downloadImageAsBase64(url: string): Promise<{ base64: string, mimeType: string } | null> {\n try {\n const res = await fetch(url, { redirect: 'follow' })\n if (!res.ok)\n return null\n\n const contentType = res.headers.get('content-type') ?? 'image/png'\n const mimeType = contentType.split(';')[0].trim()\n const buffer = Buffer.from(await res.arrayBuffer())\n return { base64: buffer.toString('base64'), mimeType }\n }\n catch {\n return null\n }\n}\n\n/**\n * Extract image URLs from HTML string.\n */\nfunction extractImageUrls(html: string): string[] {\n const imgRegex = /<img[^>]+src=\"([^\"]+)\"[^>]*>/g\n return Array.from(html.matchAll(imgRegex), m => m[1])\n .map(url => url.replace(/&amp;/g, '&'))\n}\n\nexport async function handleGetIssueDetail(\n input: GetIssueDetailInput,\n adapters: Map<string, BaseAdapter>,\n defaultSource?: string,\n) {\n const sourceType = input.source ?? defaultSource\n if (!sourceType) {\n throw new Error('No source specified and no default source configured')\n }\n\n const adapter = adapters.get(sourceType)\n if (!adapter) {\n throw new Error(\n `Source \"${sourceType}\" is not configured. Available: ${[...adapters.keys()].join(', ')}`,\n )\n }\n\n const detail = await adapter.getIssueDetail({ issueId: input.issueId })\n\n // Extract and download images from description\n const imageUrls = detail.descriptionRich ? extractImageUrls(detail.descriptionRich) : []\n const imageResults = await Promise.all(imageUrls.map(url => downloadImageAsBase64(url)))\n\n // Build MCP content: text first, then embedded images\n const content: Array<{ type: 'text', text: string } | { type: 'image', data: string, mimeType: string }> = [\n { type: 'text' as const, text: formatIssueDetail(detail) },\n ]\n\n for (let i = 0; i < imageResults.length; i++) {\n const img = imageResults[i]\n if (img) {\n content.push({\n type: 'image' as const,\n data: img.base64,\n mimeType: img.mimeType,\n })\n }\n }\n\n return { content }\n}\n\nfunction formatIssueDetail(detail: IssueDetail): string {\n const lines = [\n `# ${detail.name}`,\n '',\n `- **Key**: ${detail.key}`,\n `- **UUID**: ${detail.uuid}`,\n `- **Type**: ${detail.issueTypeName}`,\n `- **Status**: ${detail.statusName} (${detail.statusCategory})`,\n `- **Priority**: ${detail.priorityValue ?? 'N/A'}`,\n `- **Severity**: ${detail.severityLevel ?? 'N/A'}`,\n `- **Assignee**: ${detail.assignName ?? 'Unassigned'}`,\n `- **Owner**: ${detail.ownerName ?? 'Unknown'}`,\n `- **Solver**: ${detail.solverName ?? 'Unassigned'}`,\n ]\n\n if (detail.projectName)\n lines.push(`- **Project**: ${detail.projectName}`)\n if (detail.sprintName)\n lines.push(`- **Sprint**: ${detail.sprintName}`)\n if (detail.deadline)\n lines.push(`- **Deadline**: ${detail.deadline}`)\n\n lines.push('', '## Description', '')\n if (detail.descriptionRich) {\n lines.push(detail.descriptionRich)\n }\n else if (detail.descriptionText) {\n lines.push(detail.descriptionText)\n }\n else {\n lines.push('_No description_')\n }\n\n return lines.join('\\n')\n}\n","import type { BaseAdapter } from '../adapters/base.js'\nimport type { RelatedIssue } from '../types/requirement.js'\nimport { z } from 'zod/v4'\n\nexport const GetRelatedIssuesSchema = z.object({\n taskId: z.string().describe('The parent task ID or key (e.g. \"HRL2p8rTX4mQ9xMv\" or \"task-HRL2p8rTX4mQ9xMv\")'),\n source: z.string().optional().describe('Source to fetch from. If omitted, uses the default source.'),\n})\n\nexport type GetRelatedIssuesInput = z.infer<typeof GetRelatedIssuesSchema>\n\nexport async function handleGetRelatedIssues(\n input: GetRelatedIssuesInput,\n adapters: Map<string, BaseAdapter>,\n defaultSource?: string,\n) {\n const sourceType = input.source ?? defaultSource\n if (!sourceType) {\n throw new Error('No source specified and no default source configured')\n }\n\n const adapter = adapters.get(sourceType)\n if (!adapter) {\n throw new Error(\n `Source \"${sourceType}\" is not configured. Available: ${[...adapters.keys()].join(', ')}`,\n )\n }\n\n const issues = await adapter.getRelatedIssues({ taskId: input.taskId })\n\n return {\n content: [{ type: 'text' as const, text: formatRelatedIssues(issues) }],\n }\n}\n\nfunction formatRelatedIssues(issues: RelatedIssue[]): string {\n const lines = [\n `Found **${issues.length}** pending defects:`,\n '',\n ]\n\n if (issues.length === 0) {\n lines.push('No pending defects found for this task.')\n return lines.join('\\n')\n }\n\n // Group by assignee name\n const grouped = new Map<string, RelatedIssue[]>()\n for (const issue of issues) {\n const assignee = issue.assignName ?? 'Unassigned'\n if (!grouped.has(assignee))\n grouped.set(assignee, [])\n grouped.get(assignee)!.push(issue)\n }\n\n for (const [assignee, group] of grouped) {\n lines.push(`## ${assignee} (${group.length})`)\n lines.push('')\n for (const issue of group) {\n lines.push(`### ${issue.key}: ${issue.name}`)\n lines.push(`- Status: ${issue.statusName} | Priority: ${issue.priorityValue ?? 'N/A'}`)\n if (issue.projectName) {\n lines.push(`- Project: ${issue.projectName}`)\n }\n lines.push('')\n }\n }\n\n return lines.join('\\n')\n}\n","import type { BaseAdapter } from '../adapters/base.js'\nimport { z } from 'zod/v4'\n\nexport const GetRequirementSchema = z.object({\n id: z.string().describe('The requirement/issue ID'),\n source: z.string().optional().describe('Source to fetch from. If omitted, uses the default source.'),\n})\n\nexport type GetRequirementInput = z.infer<typeof GetRequirementSchema>\n\nexport async function handleGetRequirement(\n input: GetRequirementInput,\n adapters: Map<string, BaseAdapter>,\n defaultSource?: string,\n) {\n const sourceType = input.source ?? defaultSource\n if (!sourceType) {\n throw new Error('No source specified and no default source configured')\n }\n\n const adapter = adapters.get(sourceType)\n if (!adapter) {\n throw new Error(\n `Source \"${sourceType}\" is not configured. Available: ${[...adapters.keys()].join(', ')}`,\n )\n }\n\n const requirement = await adapter.getRequirement({ id: input.id })\n\n return {\n content: [\n {\n type: 'text' as const,\n text: formatRequirement(requirement),\n },\n ],\n }\n}\n\nfunction formatRequirement(req: import('../types/requirement.js').Requirement): string {\n const lines = [\n `# ${req.title}`,\n '',\n `- **ID**: ${req.id}`,\n `- **Source**: ${req.source}`,\n `- **Status**: ${req.status}`,\n `- **Priority**: ${req.priority}`,\n `- **Type**: ${req.type}`,\n `- **Assignee**: ${req.assignee ?? 'Unassigned'}`,\n `- **Reporter**: ${req.reporter || 'Unknown'}`,\n ]\n\n if (req.createdAt) {\n lines.push(`- **Created**: ${req.createdAt}`)\n }\n if (req.updatedAt) {\n lines.push(`- **Updated**: ${req.updatedAt}`)\n }\n\n if (req.dueDate) {\n lines.push(`- **Due**: ${req.dueDate}`)\n }\n\n if (req.labels.length > 0) {\n lines.push(`- **Labels**: ${req.labels.join(', ')}`)\n }\n\n lines.push('', '## Description', '', req.description || '_No description_')\n\n if (req.attachments.length > 0) {\n lines.push('', '## Attachments')\n for (const att of req.attachments) {\n lines.push(`- [${att.name}](${att.url}) (${att.mimeType}, ${att.size} bytes)`)\n }\n }\n\n return lines.join('\\n')\n}\n","import type { BaseAdapter } from '../adapters/base.js'\nimport type { McpConfig } from '../types/config.js'\n\nexport async function handleListSources(\n adapters: Map<string, BaseAdapter>,\n config: McpConfig,\n) {\n const lines = ['# Configured Sources', '']\n\n if (adapters.size === 0) {\n lines.push('No sources configured.')\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n }\n }\n\n for (const [type, adapter] of adapters) {\n const isDefault = config.defaultSource === type\n const sourceConfig = config.sources[adapter.sourceType]\n lines.push(`## ${type}${isDefault ? ' (default)' : ''}`)\n lines.push(`- **API Base**: ${sourceConfig?.apiBase ?? 'N/A'}`)\n lines.push(`- **Auth Type**: ${sourceConfig?.auth.type ?? 'N/A'}`)\n lines.push('')\n }\n\n if (config.defaultSource) {\n lines.push(`> Default source: **${config.defaultSource}**`)\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n }\n}\n","import type { BaseAdapter } from '../adapters/base.js'\nimport { z } from 'zod/v4'\n\nexport const SearchRequirementsSchema = z.object({\n query: z.string().describe('Search keywords'),\n source: z.string().optional().describe('Source to search. If omitted, searches the default source.'),\n page: z.number().int().min(1).optional().describe('Page number (default: 1)'),\n pageSize: z.number().int().min(1).max(50).optional().describe('Results per page (default: 20, max: 50)'),\n})\n\nexport type SearchRequirementsInput = z.infer<typeof SearchRequirementsSchema>\n\nfunction formatStatusMarker(status: string): string {\n return `[${status.toUpperCase()}]`\n}\n\nexport async function handleSearchRequirements(\n input: SearchRequirementsInput,\n adapters: Map<string, BaseAdapter>,\n defaultSource?: string,\n) {\n const sourceType = input.source ?? defaultSource\n if (!sourceType) {\n throw new Error('No source specified and no default source configured')\n }\n\n const adapter = adapters.get(sourceType)\n if (!adapter) {\n throw new Error(\n `Source \"${sourceType}\" is not configured. Available: ${[...adapters.keys()].join(', ')}`,\n )\n }\n\n const result = await adapter.searchRequirements({\n query: input.query,\n page: input.page,\n pageSize: input.pageSize,\n })\n\n const lines = [\n `Found **${result.total}** items (page ${result.page}/${Math.ceil(result.total / result.pageSize) || 1}):`,\n '',\n ]\n\n if (/\\u6211.*\\u7F3A\\u9677|bug|\\u6211.*\\u4EFB\\u52A1/i.test(input.query)) {\n lines.push(`Query: ${input.query}`)\n lines.push('Use an item ID or number in the next step to fetch detail.')\n lines.push('')\n }\n\n for (const item of result.items) {\n lines.push(`### ${formatStatusMarker(item.status)} ${item.id}: ${item.title}`)\n lines.push(`- Status: ${item.status} | Priority: ${item.priority} | Type: ${item.type}`)\n lines.push(`- Assignee: ${item.assignee ?? 'Unassigned'}`)\n const desc = item.description\n ? (item.description.length > 200 ? `${item.description.slice(0, 200)}...` : item.description)\n : '(empty)'\n lines.push(`- Content: ${desc}`)\n lines.push('')\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: lines.join('\\n'),\n },\n ],\n }\n}\n","import type { BaseAdapter } from './adapters/index.js'\nimport type { LoadConfigResult } from './config/loader.js'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { createAdapter } from './adapters/index.js'\nimport { loadConfig } from './config/loader.js'\nimport { GetIssueDetailSchema, handleGetIssueDetail } from './tools/get-issue-detail.js'\nimport { GetRelatedIssuesSchema, handleGetRelatedIssues } from './tools/get-related-issues.js'\nimport { GetRequirementSchema, handleGetRequirement } from './tools/get-requirement.js'\nimport { handleListSources } from './tools/list-sources.js'\nimport { handleSearchRequirements, SearchRequirementsSchema } from './tools/search-requirements.js'\n\n/**\n * Load .env file into process.env (if it exists).\n * Searches from cwd upward, same as config loader.\n */\nfunction loadEnvFile() {\n let dir = process.cwd()\n while (true) {\n const envPath = resolve(dir, '.env')\n if (existsSync(envPath)) {\n const content = readFileSync(envPath, 'utf-8')\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#'))\n continue\n const eqIndex = trimmed.indexOf('=')\n if (eqIndex === -1)\n continue\n const key = trimmed.slice(0, eqIndex).trim()\n let value = trimmed.slice(eqIndex + 1).trim()\n // Strip surrounding quotes\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith('\\'') && value.endsWith('\\''))) {\n value = value.slice(1, -1)\n }\n if (!process.env[key]) {\n process.env[key] = value\n }\n }\n return\n }\n const parent = dirname(dir)\n if (parent === dir)\n break\n dir = parent\n }\n}\n\nasync function main() {\n // Load .env before anything else\n loadEnvFile()\n\n // Load config\n let config: LoadConfigResult\n try {\n config = loadConfig()\n }\n catch (err) {\n console.error(`[requirements-mcp] ${(err as Error).message}`)\n process.exit(1)\n }\n\n // Create adapters for enabled sources\n const adapters = new Map<string, BaseAdapter>()\n for (const source of config.sources) {\n const adapter = createAdapter(source.type, source.config, source.resolvedAuth)\n adapters.set(source.type, adapter)\n }\n\n // Create MCP server\n const server = new McpServer({\n name: 'ai-dev-requirements',\n version: '0.1.0',\n })\n\n // Register tools\n server.tool(\n 'get_requirement',\n 'Fetch a single requirement/issue by its ID from a configured source (ONES)',\n GetRequirementSchema.shape,\n async (params) => {\n try {\n return await handleGetRequirement(params, adapters, config.config.defaultSource)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n server.tool(\n 'search_requirements',\n 'Search for requirements/issues by keywords across a configured source',\n SearchRequirementsSchema.shape,\n async (params) => {\n try {\n return await handleSearchRequirements(params, adapters, config.config.defaultSource)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n server.tool(\n 'list_sources',\n 'List all configured requirement sources and their status',\n {},\n async () => {\n try {\n return await handleListSources(adapters, config.config)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n server.tool(\n 'get_related_issues',\n 'Get pending defect issues (bugs) related to a requirement task. Returns all pending defects grouped by assignee (current user first).',\n GetRelatedIssuesSchema.shape,\n async (params) => {\n try {\n return await handleGetRelatedIssues(params, adapters, config.config.defaultSource)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n server.tool(\n 'get_issue_detail',\n 'Get detailed information about a specific issue/defect including description, rich text, and images',\n GetIssueDetailSchema.shape,\n async (params) => {\n try {\n return await handleGetIssueDetail(params, adapters, config.config.defaultSource)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n // Start stdio transport\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nmain().catch((err) => {\n console.error('[requirements-mcp] Fatal error:', err)\n process.exit(1)\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/utils/map-status.ts","../src/adapters/base.ts","../src/adapters/ones.ts","../src/adapters/index.ts","../src/config/loader.ts","../src/tools/get-issue-detail.ts","../src/tools/get-related-issues.ts","../src/tools/get-requirement.ts","../src/tools/get-testcases.ts","../src/tools/list-sources.ts","../src/tools/search-requirements.ts","../src/index.ts"],"sourcesContent":["import type { RequirementPriority, RequirementStatus, RequirementType } from '../types/requirement.js'\n\n// --- ONES status mapping ---\nconst ONES_STATUS_MAP: Record<string, RequirementStatus> = {\n to_do: 'open',\n in_progress: 'in_progress',\n done: 'done',\n closed: 'closed',\n}\n\n// --- Priority mappings ---\nconst ONES_PRIORITY_MAP: Record<string, RequirementPriority> = {\n urgent: 'critical',\n high: 'high',\n normal: 'medium',\n medium: 'medium',\n low: 'low',\n}\n\n// --- Type mappings ---\nconst ONES_TYPE_MAP: Record<string, RequirementType> = {\n demand: 'feature',\n 需求: 'feature',\n task: 'task',\n 任务: 'task',\n bug: 'bug',\n 缺陷: 'bug',\n story: 'story',\n 子任务: 'task',\n 工单: 'task',\n 测试任务: 'task',\n}\n\nexport function mapOnesStatus(status: string): RequirementStatus {\n return ONES_STATUS_MAP[status.toLowerCase()] ?? 'open'\n}\n\nexport function mapOnesPriority(priority: string): RequirementPriority {\n return ONES_PRIORITY_MAP[priority.toLowerCase()] ?? 'medium'\n}\n\nexport function mapOnesType(type: string): RequirementType {\n return ONES_TYPE_MAP[type.toLowerCase()] ?? 'task'\n}\n","import type { SourceConfig } from '../types/config.js'\nimport type { IssueDetail, RelatedIssue, Requirement, SearchResult, SourceType, TestCaseResult } from '../types/requirement.js'\n\nexport interface GetRequirementParams {\n id: string\n}\n\nexport interface SearchRequirementsParams {\n query: string\n page?: number\n pageSize?: number\n}\n\nexport interface GetRelatedIssuesParams {\n taskId: string\n}\n\nexport interface GetIssueDetailParams {\n issueId: string\n}\n\nexport interface GetTestcasesParams {\n taskNumber: number\n libraryUuid?: string\n}\n\n/**\n * Abstract base class for source adapters.\n * Each adapter implements platform-specific logic for fetching requirements.\n */\nexport abstract class BaseAdapter {\n readonly sourceType: SourceType\n protected readonly config: SourceConfig\n protected readonly resolvedAuth: Record<string, string>\n\n constructor(\n sourceType: SourceType,\n config: SourceConfig,\n resolvedAuth: Record<string, string>,\n ) {\n this.sourceType = sourceType\n this.config = config\n this.resolvedAuth = resolvedAuth\n }\n\n /**\n * Fetch a single requirement by its ID.\n */\n abstract getRequirement(params: GetRequirementParams): Promise<Requirement>\n\n /**\n * Search requirements by query string.\n */\n abstract searchRequirements(params: SearchRequirementsParams): Promise<SearchResult>\n\n abstract getRelatedIssues(params: GetRelatedIssuesParams): Promise<RelatedIssue[]>\n\n abstract getIssueDetail(params: GetIssueDetailParams): Promise<IssueDetail>\n\n abstract getTestcases(params: GetTestcasesParams): Promise<TestCaseResult>\n}\n","import type { SourceConfig } from '../types/config.js'\nimport type { IssueDetail, RelatedIssue, Requirement, SearchResult, SourceType, TestCase, TestCaseResult, TestCaseStep } from '../types/requirement.js'\n\nimport type { GetIssueDetailParams, GetRelatedIssuesParams, GetRequirementParams, GetTestcasesParams, SearchRequirementsParams } from './base.js'\nimport crypto from 'node:crypto'\nimport { mapOnesPriority, mapOnesStatus, mapOnesType } from '../utils/map-status.js'\nimport { BaseAdapter } from './base.js'\n\n// ============ ONES GraphQL types ============\n\ninterface OnesTaskNode {\n key?: string\n uuid: string\n number: number\n name: string\n status: { uuid: string, name: string, category?: string }\n priority?: { value: string }\n issueType?: { uuid: string, name: string, detailType?: number }\n assign?: { uuid: string, name: string } | null\n owner?: { uuid: string, name: string } | null\n project?: { uuid: string, name: string }\n parent?: { uuid: string, number?: number, issueType?: { uuid: string, name: string } } | null\n relatedTasks?: OnesRelatedTask[]\n relatedWikiPages?: OnesWikiPage[]\n relatedWikiPagesCount?: number\n path?: string\n}\n\ninterface OnesWikiPage {\n uuid: string\n title: string\n referenceType?: number\n subReferenceType?: string\n errorMessage?: string\n}\n\ninterface OnesRelatedTask {\n uuid: string\n number: number\n name: string\n issueType: { uuid: string, name: string }\n status: { uuid: string, name: string, category?: string }\n assign?: { uuid: string, name: string } | null\n}\n\ninterface OnesIssueTypeNode {\n uuid: string\n name: string\n detailType: number\n}\n\ninterface OnesTeamUserNode {\n uuid?: string\n name?: string\n user?: {\n uuid?: string\n name?: string\n }\n org_user?: {\n org_user_uuid?: string\n name?: string\n }\n orgUser?: {\n uuid?: string\n name?: string\n }\n orgUserUuid?: string\n org_user_uuid?: string\n}\n\ninterface OnesTokenResponse {\n access_token: string\n token_type: string\n expires_in: number\n}\n\ninterface OnesLoginResponse {\n sid: string\n auth_user_uuid: string\n org_users: Array<{\n region_uuid: string\n org_uuid: string\n org_user: { org_user_uuid: string, name: string }\n org: { org_uuid: string, name: string }\n }>\n}\n\ninterface OnesSession {\n accessToken: string\n teamUuid: string\n orgUuid: string\n userUuid: string\n expiresAt: number\n}\n\n// ============ GraphQL queries ============\n\nconst TASK_DETAIL_QUERY = `\n query Task($key: Key) {\n task(key: $key) {\n key uuid number name\n issueType { uuid name }\n status { uuid name category }\n priority { value }\n assign { uuid name }\n owner { uuid name }\n project { uuid name }\n parent { uuid number issueType { uuid name } }\n relatedTasks {\n uuid number name\n issueType { uuid name }\n status { uuid name category }\n assign { uuid name }\n }\n relatedWikiPages {\n uuid\n title\n referenceType\n subReferenceType\n errorMessage\n }\n relatedWikiPagesCount\n }\n }\n`\n\nconst SEARCH_TASKS_QUERY = `\n query GROUP_TASK_DATA($groupBy: GroupBy, $groupOrderBy: OrderBy, $orderBy: OrderBy, $filterGroup: [Filter!], $search: Search, $pagination: Pagination, $limit: Int) {\n buckets(groupBy: $groupBy, orderBy: $groupOrderBy, pagination: $pagination, filter: $search) {\n key\n tasks(filterGroup: $filterGroup, orderBy: $orderBy, limit: $limit, includeAncestors: { pathField: \"path\" }) {\n key uuid number name\n issueType { uuid name detailType }\n status { uuid name category }\n priority { value }\n assign { uuid name }\n project { uuid name }\n }\n }\n }\n`\n\nconst ISSUE_TYPES_QUERY = `\n query IssueTypes($orderBy: OrderBy) {\n issueTypes(orderBy: $orderBy) {\n uuid\n name\n detailType\n }\n }\n`\n\n// Query to find a task by its number\nconst TASK_BY_NUMBER_QUERY = SEARCH_TASKS_QUERY\nconst RELATED_TASKS_QUERY = `\n query Task($key: Key) {\n task(key: $key) {\n key\n relatedTasks {\n key\n uuid\n name\n path\n deadline\n project { uuid name }\n priority { value }\n issueType {\n key uuid name detailType\n }\n subIssueType {\n key uuid name detailType\n }\n status {\n uuid name category\n }\n assign {\n uuid name\n }\n sprint {\n name uuid\n }\n statusCategory\n }\n }\n }\n`\n\nconst ISSUE_DETAIL_QUERY = `\n query Task($key: Key) {\n task(key: $key) {\n key uuid\n description\n descriptionText\n desc_rich: description\n name\n issueType { key uuid name detailType }\n subIssueType { key uuid name detailType }\n status { uuid name category }\n priority { value }\n assign { uuid name }\n owner { uuid name }\n solver { uuid name }\n project { uuid name }\n severityLevel { value }\n deadline(unit: ONESDATE)\n sprint { name uuid }\n }\n }\n`\n\nconst DEFAULT_STATUS_NOT_IN = ['FgMGkcaq', 'NvRwHBSo', 'Dn3k8ffK', 'TbmY2So5']\n\n// ============ Testcase GraphQL queries ============\n\nconst TESTCASE_LIBRARY_LIST_QUERY = `\n query Q {\n testcaseLibraries {\n uuid name key\n testcaseCaseCount\n }\n }\n`\n\nconst TESTCASE_MODULE_SEARCH_QUERY = `\n query Q($filter: Filter) {\n testcaseModules(filter: $filter) {\n uuid name key\n parent { uuid name }\n }\n }\n`\n\nconst TESTCASE_LIST_PAGED_QUERY = `\n query PAGED_LIBRARY_TESTCASE_LIST($testCaseFilter: Filter, $pagination: Pagination) {\n buckets(groupBy: {testcaseCases: {}}, pagination: $pagination) {\n testcaseCases(filterGroup: $testCaseFilter, limit: 10000) {\n uuid name key id\n priority { uuid value }\n type { uuid value }\n assign { uuid name }\n testcaseModule { uuid }\n }\n key\n pageInfo { count totalCount hasNextPage endCursor }\n }\n }\n`\n\nconst TESTCASE_DETAIL_QUERY = `\n query QUERY_TESTCASES_DETAIL($testCaseFilter: Filter, $stepFilter: Filter) {\n testcaseCases(filter: $testCaseFilter) {\n uuid name key id condition desc path\n assign { uuid name }\n priority { uuid value }\n type { uuid value }\n testcaseLibrary { uuid }\n testcaseModule { uuid }\n relatedTasks { uuid name number }\n }\n testcaseCaseSteps(filter: $stepFilter, orderBy: { index: ASC }) {\n key uuid\n testcaseCase { uuid }\n desc result index\n }\n }\n`\n\n// ============ Helpers ============\n\nfunction _getTaskStatusPriority(task: Pick<OnesTaskNode, 'status'>): number {\n const category = task.status?.category\n const name = task.status?.name\n\n if (category === 'to_do')\n return 0\n\n if (category === 'in_progress' && name === '修复中')\n return 1\n\n return Number.POSITIVE_INFINITY\n}\n\nfunction _isCommonTaskIssueType(task: Pick<OnesTaskNode, 'issueType'>): boolean {\n const detailType = task.issueType?.detailType\n\n if (detailType === 2 || detailType === 3)\n return true\n\n return task.issueType?.name === '任务' || task.issueType?.name === '缺陷'\n}\n\ntype OnesSearchIntent = 'all_bugs' | 'all_tasks' | 'keyword'\n\nfunction parseOnesSearchIntent(query: string): OnesSearchIntent {\n if (!query)\n return 'keyword'\n\n const normalized = query.toLowerCase()\n\n if (query.includes('\\u7F3A\\u9677') || normalized.includes('bug'))\n return 'all_bugs'\n\n if (query.includes('\\u4EFB\\u52A1'))\n return 'all_tasks'\n\n return 'keyword'\n}\n\nfunction extractAssigneeName(query: string, intent: OnesSearchIntent): string | null {\n if (intent === 'keyword')\n return null\n\n const trimmed = query.trim()\n if (!trimmed)\n return null\n\n const ownerStyleMatch = trimmed.match(/\\u8D1F\\u8D23\\u4EBA\\u4E3A(.+?)\\u7684?(?:\\u7F3A\\u9677|bug)$/i)\n if (ownerStyleMatch?.[1]) {\n return ownerStyleMatch[1].trim()\n }\n\n const genericMatch = trimmed.match(/^(查询)?(.+?)的(?:缺陷|bug|任务)$/i)\n const candidate = genericMatch?.[2]?.trim()\n if (!candidate || candidate.includes('我')) {\n return null\n }\n\n return candidate\n}\n\nfunction extractNamedAssignee(query: string, intent: OnesSearchIntent): string | null {\n if (intent === 'keyword')\n return null\n\n const compact = query.replace(/\\s+/g, '').trim()\n if (!compact)\n return null\n\n 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)\n if (ownerStyleMatch?.[1]) {\n return ownerStyleMatch[1].trim()\n }\n\n const genericMatch = compact.match(/^(?:\\u67E5\\u8BE2|\\u67E5\\u627E|\\u641C\\u7D22)?(.+?)\\u7684?(?:\\u7F3A\\u9677|bug|\\u4EFB\\u52A1)$/i)\n const candidate = genericMatch?.[1]?.trim()\n\n if (\n !candidate\n || candidate.startsWith('\\u6211')\n || /^(?:\\u6211|\\u6211\\u7684|\\u6211\\u6240\\u6709|\\u6211\\u5168\\u90E8|\\u672C\\u4EBA|\\u5F53\\u524D\\u7528\\u6237)$/.test(candidate)\n ) {\n return null\n }\n\n return candidate\n}\n\nfunction getBugStatusPriority(task: Pick<OnesTaskNode, 'status'>): number {\n if (task.status?.category === 'to_do')\n return 0\n\n if (task.status?.category === 'in_progress')\n return 1\n\n return Number.POSITIVE_INFINITY\n}\n\nfunction isOpenOrInProgressBug(task: Pick<OnesTaskNode, 'status'>): boolean {\n const category = task.status?.category\n return category === 'to_do' || category === 'in_progress'\n}\n\nfunction extractTeamUsers(payload: unknown): Array<{ uuid: string, name: string }> {\n const record = payload && typeof payload === 'object'\n ? payload as Record<string, unknown>\n : null\n\n if (!record)\n return []\n\n const candidates = [\n record.users,\n record.items,\n record.list,\n record.results,\n (record.data as Record<string, unknown> | undefined)?.users,\n (record.data as Record<string, unknown> | undefined)?.items,\n (record.data as Record<string, unknown> | undefined)?.list,\n (record.data as Record<string, unknown> | undefined)?.results,\n ]\n\n const rawUsers = candidates.find(Array.isArray)\n if (!rawUsers)\n return []\n\n return rawUsers\n .map((item) => {\n const user = item && typeof item === 'object'\n ? item as OnesTeamUserNode\n : null\n\n if (!user)\n return null\n\n const uuid = user.uuid\n ?? user.user?.uuid\n ?? user.orgUser?.uuid\n ?? user.orgUserUuid\n ?? user.org_user_uuid\n ?? user.org_user?.org_user_uuid\n\n const name = user.name\n ?? user.user?.name\n ?? user.orgUser?.name\n ?? user.org_user?.name\n\n if (!uuid || !name)\n return null\n\n return { uuid, name }\n })\n .filter((item): item is { uuid: string, name: string } => item !== null)\n}\n\nfunction base64Url(buffer: Buffer): string {\n return buffer.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\n}\n\nfunction getSetCookies(response: Response): string[] {\n const headers = response.headers as unknown as { getSetCookie?: () => string[] }\n if (headers.getSetCookie) {\n return headers.getSetCookie()\n }\n const raw = response.headers.get('set-cookie')\n return raw ? [raw] : []\n}\n\nfunction toRequirement(task: OnesTaskNode, description = ''): Requirement {\n return {\n id: task.uuid,\n source: 'ones',\n title: `#${task.number} ${task.name}`,\n description,\n status: mapOnesStatus(task.status?.category ?? 'to_do'),\n priority: mapOnesPriority(task.priority?.value ?? 'normal'),\n type: mapOnesType(task.issueType?.name ?? '任务'),\n labels: [],\n reporter: '',\n assignee: task.assign?.name ?? null,\n // ONES GraphQL does not return timestamps; these are fetch-time placeholders\n createdAt: '',\n updatedAt: '',\n dueDate: null,\n attachments: [],\n raw: task as unknown as Record<string, unknown>,\n }\n}\n\n// ============ ONES Adapter ============\n\nexport class OnesAdapter extends BaseAdapter {\n private session: OnesSession | null = null\n private issueTypesCache: OnesIssueTypeNode[] | null = null\n\n constructor(\n sourceType: SourceType,\n config: SourceConfig,\n resolvedAuth: Record<string, string>,\n ) {\n super(sourceType, config, resolvedAuth)\n }\n\n /**\n * ONES OAuth2 PKCE login flow.\n * Reference: D:\\company code\\ones\\packages\\core\\src\\auth.ts\n */\n private async login(): Promise<OnesSession> {\n if (this.session && Date.now() < this.session.expiresAt) {\n return this.session\n }\n\n const baseUrl = this.config.apiBase\n const email = this.resolvedAuth.email\n const password = this.resolvedAuth.password\n\n if (!email || !password) {\n throw new Error('ONES auth requires email and password (ones-pkce auth type)')\n }\n\n // 1. Get encryption certificate\n const certRes = await fetch(`${baseUrl}/identity/api/encryption_cert`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: '{}',\n })\n if (!certRes.ok) {\n throw new Error(`ONES: Failed to get encryption cert: ${certRes.status}`)\n }\n const cert = (await certRes.json()) as { public_key: string }\n\n // 2. Encrypt password with RSA public key\n const encrypted = crypto.publicEncrypt(\n { key: cert.public_key, padding: crypto.constants.RSA_PKCS1_PADDING },\n Buffer.from(password, 'utf-8'),\n )\n const encryptedPassword = encrypted.toString('base64')\n\n // 3. Login\n const loginRes = await fetch(`${baseUrl}/identity/api/login`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email, password: encryptedPassword }),\n })\n if (!loginRes.ok) {\n const text = await loginRes.text().catch(() => '')\n throw new Error(`ONES: Login failed: ${loginRes.status} ${text}`)\n }\n\n const cookies = getSetCookies(loginRes)\n .map(cookie => cookie.split(';')[0])\n .join('; ')\n const loginData = (await loginRes.json()) as OnesLoginResponse\n\n // Pick org user (first one, or match by config option)\n const orgUuid = this.config.options?.orgUuid as string | undefined\n let orgUser = loginData.org_users[0]\n if (orgUuid) {\n const match = loginData.org_users.find(u => u.org_uuid === orgUuid)\n if (match)\n orgUser = match\n }\n\n // 4. PKCE: generate code verifier + challenge\n const codeVerifier = base64Url(crypto.randomBytes(32))\n const codeChallenge = base64Url(\n crypto.createHash('sha256').update(codeVerifier).digest(),\n )\n\n // 5. Authorize\n const authorizeParams = new URLSearchParams({\n client_id: 'ones.v1',\n scope: `openid offline_access ones:org:${orgUser.region_uuid}:${orgUser.org_uuid}:${orgUser.org_user.org_user_uuid}`,\n response_type: 'code',\n code_challenge_method: 'S256',\n code_challenge: codeChallenge,\n redirect_uri: `${baseUrl}/auth/authorize/callback`,\n state: `org_uuid=${orgUser.org_uuid}`,\n })\n\n const authorizeRes = await fetch(`${baseUrl}/identity/authorize`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Cookie': cookies,\n },\n body: authorizeParams.toString(),\n redirect: 'manual',\n })\n\n const authorizeLocation = authorizeRes.headers.get('location')\n if (!authorizeLocation) {\n throw new Error('ONES: Authorize response missing location header')\n }\n const authRequestId = new URL(authorizeLocation).searchParams.get('id')\n if (!authRequestId) {\n throw new Error('ONES: Cannot parse auth_request_id from authorize redirect')\n }\n\n // 6. Finalize auth request\n const finalizeRes = await fetch(`${baseUrl}/identity/api/auth_request/finalize`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json;charset=UTF-8',\n 'Cookie': cookies,\n },\n body: JSON.stringify({\n auth_request_id: authRequestId,\n region_uuid: orgUser.region_uuid,\n org_uuid: orgUser.org_uuid,\n org_user_uuid: orgUser.org_user.org_user_uuid,\n }),\n })\n if (!finalizeRes.ok) {\n const text = await finalizeRes.text().catch(() => '')\n throw new Error(`ONES: Finalize failed: ${finalizeRes.status} ${text}`)\n }\n\n // 7. Callback to get authorization code\n const callbackRes = await fetch(\n `${baseUrl}/identity/authorize/callback?id=${authRequestId}&lang=zh`,\n {\n method: 'GET',\n headers: { Cookie: cookies },\n redirect: 'manual',\n },\n )\n\n const callbackLocation = callbackRes.headers.get('location')\n if (!callbackLocation) {\n throw new Error('ONES: Callback response missing location header')\n }\n const code = new URL(callbackLocation).searchParams.get('code')\n if (!code) {\n throw new Error('ONES: Cannot parse authorization code from callback redirect')\n }\n\n // 8. Exchange code for token\n const tokenRes = await fetch(`${baseUrl}/identity/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Cookie': cookies,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: 'ones.v1',\n code,\n code_verifier: codeVerifier,\n redirect_uri: `${baseUrl}/auth/authorize/callback`,\n }).toString(),\n })\n if (!tokenRes.ok) {\n const text = await tokenRes.text().catch(() => '')\n throw new Error(`ONES: Token exchange failed: ${tokenRes.status} ${text}`)\n }\n\n const token = (await tokenRes.json()) as OnesTokenResponse\n\n // 9. Get teams to find teamUuid\n const teamsRes = await fetch(\n `${baseUrl}/project/api/project/organization/${orgUser.org_uuid}/stamps/data?t=org_my_team`,\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${token.access_token}`,\n 'Content-Type': 'application/json;charset=UTF-8',\n },\n body: JSON.stringify({ org_my_team: 0 }),\n },\n )\n if (!teamsRes.ok) {\n throw new Error(`ONES: Failed to fetch teams: ${teamsRes.status}`)\n }\n\n const teamsData = (await teamsRes.json()) as {\n org_my_team?: { teams?: Array<{ uuid: string, name: string }> }\n }\n const teams = teamsData.org_my_team?.teams ?? []\n\n // Pick team by config option or default to first\n const configTeamUuid = this.config.options?.teamUuid as string | undefined\n let teamUuid = teams[0]?.uuid\n if (configTeamUuid) {\n const match = teams.find(t => t.uuid === configTeamUuid)\n if (match)\n teamUuid = match.uuid\n }\n\n if (!teamUuid) {\n throw new Error('ONES: No teams found for this user')\n }\n\n this.session = {\n accessToken: token.access_token,\n teamUuid,\n orgUuid: orgUser.org_uuid,\n userUuid: orgUser.org_user.org_user_uuid,\n expiresAt: Date.now() + (token.expires_in - 60) * 1000, // refresh 60s early\n }\n\n return this.session\n }\n\n /**\n * Execute a GraphQL query against ONES project API.\n */\n private async graphql<T>(query: string, variables: Record<string, unknown>, tag?: string): Promise<T> {\n const session = await this.login()\n const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/items/graphql${tag ? `?t=${encodeURIComponent(tag)}` : ''}`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${session.accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ query, variables }),\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`ONES GraphQL error: ${response.status} ${text}`)\n }\n\n return response.json() as Promise<T>\n }\n\n private async fetchIssueTypes(): Promise<OnesIssueTypeNode[]> {\n if (this.issueTypesCache)\n return this.issueTypesCache\n\n const data = await this.graphql<{ data?: { issueTypes?: OnesIssueTypeNode[] } }>(\n ISSUE_TYPES_QUERY,\n { orderBy: { namePinyin: 'ASC' } },\n 'issueTypes',\n )\n\n this.issueTypesCache = data.data?.issueTypes ?? []\n return this.issueTypesCache\n }\n\n private async searchTeamUsers(keyword: string): Promise<Array<{ uuid: string, name: string }>> {\n const session = await this.login()\n const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/users/search`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${session.accessToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n keyword,\n status: [1],\n team_member_status: [1, 4],\n types: [1, 10],\n }),\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`ONES user search error: ${response.status} ${text}`)\n }\n\n return extractTeamUsers(await response.json())\n }\n\n private async resolveAssigneeUuid(name: string): Promise<string | null> {\n const trimmed = name.trim()\n if (!trimmed)\n return null\n\n const users = await this.searchTeamUsers(trimmed)\n const exactMatch = users.find(user => user.name === trimmed)\n if (exactMatch)\n return exactMatch.uuid\n\n const normalizedTarget = trimmed.toLowerCase()\n const fuzzyMatch = users.find(user => user.name.toLowerCase().includes(normalizedTarget))\n return fuzzyMatch?.uuid ?? null\n }\n\n /**\n * Fetch task info via REST API (includes description/rich fields not available in GraphQL).\n * Reference: ones/packages/core/src/tasks.ts → fetchTaskInfo\n */\n private async fetchTaskInfo(taskUuid: string): Promise<Record<string, unknown>> {\n const session = await this.login()\n const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/task/${taskUuid}/info`\n\n const response = await fetch(url, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n })\n\n if (!response.ok) {\n return {}\n }\n\n return response.json() as Promise<Record<string, unknown>>\n }\n\n /**\n * Resolve a fresh signed URL for an attachment resource via ONES attachment API.\n * Endpoint: /project/api/project/team/{teamUuid}/res/attachment/{resourceUuid}\n * Returns a redirect URL with a fresh OSS signature.\n */\n private async getAttachmentUrl(resourceUuid: string): Promise<string | null> {\n const session = await this.login()\n const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/res/attachment/${resourceUuid}?op=${encodeURIComponent('imageMogr2/auto-orient')}`\n\n try {\n // First try with redirect: 'manual' to capture 302 Location header\n const manualRes = await fetch(url, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n redirect: 'manual',\n })\n\n if (manualRes.status === 302 || manualRes.status === 301) {\n const location = manualRes.headers.get('location')\n if (location)\n return location\n }\n\n // Fallback: follow redirects and use the final URL\n const followRes = await fetch(url, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n redirect: 'follow',\n })\n\n // If redirected, response.url will be the final signed URL\n if (followRes.url && followRes.url !== url) {\n return followRes.url\n }\n\n if (followRes.ok) {\n const text = await followRes.text()\n if (text.startsWith('http')) {\n return text.trim()\n }\n try {\n const data = JSON.parse(text) as { url?: string }\n return data.url ?? null\n }\n catch {\n return null\n }\n }\n\n console.error(`[getAttachmentUrl] Failed for resource ${resourceUuid}: status ${followRes.status}`)\n return null\n }\n catch (err) {\n console.error(`[getAttachmentUrl] Error for resource ${resourceUuid}:`, err)\n return null\n }\n }\n\n /**\n * Replace stale image URLs in HTML with fresh signed URLs from the attachment API.\n * Extracts data-uuid from <img> tags and resolves fresh URLs in parallel.\n */\n private async refreshImageUrls(html: string): Promise<string> {\n if (!html)\n return html\n\n // Extract all img tags with data-uuid\n const imgRegex = /<img\\s[^>]*data-uuid=\"([^\"]+)\"[^>]*>/g\n const matches = Array.from(html.matchAll(imgRegex))\n\n if (matches.length === 0)\n return html\n\n // Resolve fresh URLs in parallel\n const replacements = await Promise.all(\n matches.map(async (match) => {\n const dataUuid = match[1]\n const freshUrl = await this.getAttachmentUrl(dataUuid)\n return { fullMatch: match[0], dataUuid, freshUrl }\n }),\n )\n\n let result = html\n for (const { fullMatch, freshUrl } of replacements) {\n if (freshUrl) {\n // Replace the src attribute in the img tag with the fresh URL\n const updatedImg = fullMatch.replace(/src=\"[^\"]*\"/, `src=\"${freshUrl}\"`)\n result = result.replace(fullMatch, updatedImg)\n }\n }\n\n return result\n }\n\n /**\n * Fetch wiki page content via REST API.\n * Endpoint: /wiki/api/wiki/team/{teamUuid}/online_page/{wikiUuid}/content\n */\n private async fetchWikiContent(wikiUuid: string): Promise<string> {\n const session = await this.login()\n const url = `${this.config.apiBase}/wiki/api/wiki/team/${session.teamUuid}/online_page/${wikiUuid}/content`\n\n const response = await fetch(url, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n })\n\n if (!response.ok) {\n return ''\n }\n\n const data = await response.json() as { content?: string }\n return data.content ?? ''\n }\n\n /**\n * Fetch a single task by UUID or number (e.g. \"#95945\" or \"95945\").\n * If a number is given, searches first to resolve the UUID.\n */\n async getRequirement(params: GetRequirementParams): Promise<Requirement> {\n let taskUuid = params.id\n\n // If the ID looks like a number (with or without #), search to find the UUID\n const numMatch = taskUuid.match(/^#?(\\d+)$/)\n if (numMatch) {\n const taskNumber = Number.parseInt(numMatch[1], 10)\n const searchData = await this.graphql<{\n data?: { buckets?: Array<{ tasks?: OnesTaskNode[] }> }\n }>(\n TASK_BY_NUMBER_QUERY,\n {\n groupBy: { tasks: {} },\n groupOrderBy: null,\n orderBy: { createTime: 'DESC' },\n filterGroup: [{ number_in: [taskNumber] }],\n search: null,\n pagination: { limit: 10, preciseCount: false },\n limit: 10,\n },\n 'group-task-data',\n )\n\n const allTasks = searchData.data?.buckets?.flatMap(b => b.tasks ?? []) ?? []\n const found = allTasks.find(t => t.number === taskNumber)\n\n if (!found) {\n throw new Error(`ONES: Task #${taskNumber} not found in current team`)\n }\n taskUuid = found.uuid\n }\n\n // Fetch GraphQL data (structure, relations, wiki pages)\n const graphqlData = await this.graphql<{ data?: { task?: OnesTaskNode } }>(\n TASK_DETAIL_QUERY,\n { key: `task-${taskUuid}` },\n 'Task',\n )\n\n const task = graphqlData.data?.task\n if (!task) {\n throw new Error(`ONES: Task \"${taskUuid}\" not found`)\n }\n\n // Fetch wiki page content for related requirement docs (in parallel)\n const wikiPages = task.relatedWikiPages ?? []\n const wikiContents = await Promise.all(\n wikiPages\n .filter(w => !w.errorMessage)\n .map(async (wiki) => {\n const content = await this.fetchWikiContent(wiki.uuid)\n return { title: wiki.title, uuid: wiki.uuid, content }\n }),\n )\n\n // Build description: task info + wiki requirement docs\n const parts: string[] = []\n\n // Task basic info\n parts.push(`# #${task.number} ${task.name}`)\n parts.push('')\n parts.push(`- **Type**: ${task.issueType?.name ?? 'Unknown'}`)\n parts.push(`- **Status**: ${task.status?.name ?? 'Unknown'}`)\n parts.push(`- **Assignee**: ${task.assign?.name ?? 'Unassigned'}`)\n if (task.owner?.name) {\n parts.push(`- **Owner**: ${task.owner.name}`)\n }\n if (task.project?.name) {\n parts.push(`- **Project**: ${task.project.name}`)\n }\n parts.push(`- **UUID**: ${task.uuid}`)\n\n // Related tasks\n if (task.relatedTasks?.length) {\n parts.push('')\n parts.push('## Related Tasks')\n for (const related of task.relatedTasks) {\n const assignee = related.assign?.name ?? 'Unassigned'\n parts.push(`- #${related.number} ${related.name} [${related.issueType?.name}] (${related.status?.name}) — ${assignee}`)\n }\n }\n\n // Parent task\n if (task.parent?.uuid) {\n parts.push('')\n parts.push('## Parent Task')\n parts.push(`- UUID: ${task.parent.uuid}`)\n if (task.parent.number) {\n parts.push(`- Number: #${task.parent.number}`)\n }\n }\n\n // Wiki requirement documents (the core requirement content)\n if (wikiContents.length > 0) {\n parts.push('')\n parts.push('---')\n parts.push('')\n parts.push('## Requirement Documents')\n for (const wiki of wikiContents) {\n parts.push('')\n parts.push(`### ${wiki.title}`)\n parts.push('')\n if (wiki.content) {\n parts.push(wiki.content)\n }\n else {\n parts.push('(No content available)')\n }\n }\n }\n\n const req = toRequirement(task, parts.join('\\n'))\n\n return req\n }\n\n /**\n * Search tasks assigned to current user via GraphQL.\n * Uses keyword-based local filtering (matching ONES reference implementation).\n */\n async searchRequirements(params: SearchRequirementsParams): Promise<SearchResult> {\n const page = params.page ?? 1\n const pageSize = params.pageSize ?? 50\n const intent = parseOnesSearchIntent(params.query)\n const assigneeName = extractNamedAssignee(params.query, intent) ?? extractAssigneeName(params.query, intent)\n const assigneeUuid = assigneeName\n ? await this.resolveAssigneeUuid(assigneeName)\n : null\n\n if (assigneeName && !assigneeUuid) {\n return {\n items: [],\n total: 0,\n page,\n pageSize,\n }\n }\n\n let bugTypeUuids: string[] = []\n let taskTypeUuids: string[] = []\n\n if (intent === 'all_bugs' || intent === 'all_tasks') {\n const issueTypes = await this.fetchIssueTypes()\n bugTypeUuids = issueTypes.filter(item => item.detailType === 3).map(item => item.uuid)\n taskTypeUuids = issueTypes.filter(item => item.detailType === 2).map(item => item.uuid)\n }\n\n const filter: Record<string, unknown> = {\n status_notIn: DEFAULT_STATUS_NOT_IN,\n }\n\n if (assigneeName) {\n filter.assign_in = [assigneeUuid]\n }\n else {\n filter.assign_in = ['${currentUser}']\n }\n\n if (intent === 'all_bugs')\n filter.issueType_in = bugTypeUuids\n\n if (intent === 'all_tasks')\n filter.issueType_in = taskTypeUuids\n\n const data = await this.graphql<{\n data?: {\n buckets?: Array<{\n key: string\n tasks?: OnesTaskNode[]\n }>\n }\n }>(\n SEARCH_TASKS_QUERY,\n {\n groupBy: { tasks: {} },\n groupOrderBy: null,\n orderBy: { position: 'ASC', createTime: 'DESC' },\n filterGroup: [filter],\n search: null,\n pagination: { limit: pageSize * page, preciseCount: false },\n limit: 1000,\n },\n 'group-task-data',\n )\n\n let tasks = data.data?.buckets?.flatMap(b => b.tasks ?? []) ?? []\n\n if (intent === 'all_bugs') {\n tasks = tasks\n .filter(task => task.issueType?.uuid ? bugTypeUuids.includes(task.issueType.uuid) : false)\n .filter(task => isOpenOrInProgressBug(task))\n .sort((a, b) => getBugStatusPriority(a) - getBugStatusPriority(b))\n }\n\n if (intent === 'all_tasks') {\n // detailType = 1 的需求不属于“我的任务”列表入口。\n // 需求详情查询继续走 getRequirement(id/number),因为需求通常由产品负责人维护,\n // 当前登录用户不一定能通过 assign_in: ['${currentUser}'] 查到需求项。\n tasks = tasks.filter(task => task.issueType?.uuid ? taskTypeUuids.includes(task.issueType.uuid) : false)\n }\n\n if (assigneeUuid) {\n tasks = tasks.filter(task => task.assign?.uuid === assigneeUuid)\n }\n\n // Local keyword filter (matching ones-api.ts behavior)\n if (intent === 'keyword' && params.query) {\n const keyword = params.query.trim()\n const lower = keyword.toLowerCase()\n const numMatch = keyword.match(/^#?(\\d+)$/)\n\n if (numMatch) {\n tasks = tasks.filter(t => t.number === Number.parseInt(numMatch[1], 10))\n }\n else {\n tasks = tasks.filter(t => t.name.toLowerCase().includes(lower))\n }\n }\n\n // Paginate locally\n const total = tasks.length\n const start = (page - 1) * pageSize\n const paged = tasks.slice(start, start + pageSize)\n\n return {\n items: paged.map(t => toRequirement(t)),\n total,\n page,\n pageSize,\n }\n }\n\n async getRelatedIssues(params: GetRelatedIssuesParams): Promise<RelatedIssue[]> {\n const session = await this.login()\n\n const taskKey = params.taskId.startsWith('task-')\n ? params.taskId\n : `task-${params.taskId}`\n\n const data = await this.graphql<{\n data?: {\n task?: {\n key: string\n relatedTasks: Array<{\n key: string\n uuid: string\n name: string\n issueType: { key: string, uuid: string, name: string, detailType: number }\n subIssueType?: { key: string, uuid: string, name: string, detailType: number } | null\n status: { uuid: string, name: string, category: string }\n assign?: { uuid: string, name: string } | null\n priority?: { value: string } | null\n project?: { uuid: string, name: string } | null\n }>\n }\n }\n }>(RELATED_TASKS_QUERY, { key: taskKey }, 'Task')\n\n const relatedTasks = data.data?.task?.relatedTasks ?? []\n\n // Filter: detailType === 3 (defect) + status.category === \"to_do\" (pending)\n // Returns ALL pending defects, not just current user's\n const filtered = relatedTasks.filter((t) => {\n const isDefect = t.issueType?.detailType === 3\n || t.subIssueType?.detailType === 3\n const isTodo = t.status?.category === 'to_do'\n return isDefect && isTodo\n })\n\n // Sort: current user's defects first\n const currentUserUuid = session.userUuid\n filtered.sort((a, b) => {\n const aIsCurrent = a.assign?.uuid === currentUserUuid ? 0 : 1\n const bIsCurrent = b.assign?.uuid === currentUserUuid ? 0 : 1\n return aIsCurrent - bIsCurrent\n })\n\n return filtered.map(t => ({\n key: t.key,\n uuid: t.uuid,\n name: t.name,\n issueTypeName: t.issueType?.name ?? 'Unknown',\n statusName: t.status?.name ?? 'Unknown',\n statusCategory: t.status?.category ?? 'unknown',\n assignName: t.assign?.name ?? null,\n assignUuid: t.assign?.uuid ?? null,\n priorityValue: t.priority?.value ?? null,\n projectName: t.project?.name ?? null,\n }))\n }\n\n async getIssueDetail(params: GetIssueDetailParams): Promise<IssueDetail> {\n let issueKey: string\n\n // Support number lookup (e.g. \"98086\" or \"#98086\")\n const numMatch = params.issueId.match(/^#?(\\d+)$/)\n if (numMatch) {\n const taskNumber = Number.parseInt(numMatch[1], 10)\n const searchData = await this.graphql<{\n data?: { buckets?: Array<{ tasks?: Array<{ uuid: string, number: number }> }> }\n }>(\n SEARCH_TASKS_QUERY,\n {\n groupBy: { tasks: {} },\n groupOrderBy: null,\n orderBy: { createTime: 'DESC' },\n filterGroup: [{ number_in: [taskNumber] }],\n search: null,\n pagination: { limit: 10, preciseCount: false },\n limit: 10,\n },\n 'group-task-data',\n )\n\n const allTasks = searchData.data?.buckets?.flatMap(b => b.tasks ?? []) ?? []\n const found = allTasks.find(t => t.number === taskNumber)\n if (!found) {\n throw new Error(`ONES: Issue #${taskNumber} not found in current team`)\n }\n issueKey = `task-${found.uuid}`\n }\n else {\n issueKey = params.issueId.startsWith('task-')\n ? params.issueId\n : `task-${params.issueId}`\n }\n\n const data = await this.graphql<{\n data?: {\n task?: {\n key: string\n uuid: string\n name: string\n description: string\n descriptionText: string\n desc_rich: string\n issueType: { name: string }\n status: { name: string, category: string }\n priority?: { value: string } | null\n assign?: { uuid: string, name: string } | null\n owner?: { uuid: string, name: string } | null\n solver?: { uuid: string, name: string } | null\n project?: { uuid: string, name: string } | null\n severityLevel?: { value: string } | null\n deadline?: string | null\n sprint?: { name: string } | null\n }\n }\n }>(ISSUE_DETAIL_QUERY, { key: issueKey }, 'Task')\n\n const task = data.data?.task\n if (!task) {\n throw new Error(`ONES: Issue \"${issueKey}\" not found`)\n }\n\n // Fetch fresh description via REST API\n const taskInfo = await this.fetchTaskInfo(task.uuid)\n const rawDescription = (taskInfo.desc as string) ?? task.description ?? ''\n const rawDescRich = (taskInfo.desc_rich as string) ?? task.desc_rich ?? ''\n\n // Refresh image URLs via attachment API (signed URLs expire after 1 hour)\n const freshDescription = await this.refreshImageUrls(rawDescription)\n const freshDescRich = await this.refreshImageUrls(rawDescRich)\n\n return {\n key: task.key,\n uuid: task.uuid,\n name: task.name,\n description: freshDescription,\n descriptionRich: freshDescRich,\n descriptionText: task.descriptionText ?? '',\n issueTypeName: task.issueType?.name ?? 'Unknown',\n statusName: task.status?.name ?? 'Unknown',\n statusCategory: task.status?.category ?? 'unknown',\n assignName: task.assign?.name ?? null,\n ownerName: task.owner?.name ?? null,\n solverName: task.solver?.name ?? null,\n priorityValue: task.priority?.value ?? null,\n severityLevel: task.severityLevel?.value ?? null,\n projectName: task.project?.name ?? null,\n deadline: task.deadline ?? null,\n sprintName: task.sprint?.name ?? null,\n raw: task as unknown as Record<string, unknown>,\n }\n }\n\n async getTestcases(params: GetTestcasesParams): Promise<TestCaseResult> {\n let libraryUuid = params.libraryUuid\n ?? (this.config.options?.testcaseLibraryUuid as string)\n\n // Auto-fetch library UUID if not configured\n if (!libraryUuid) {\n const libData = await this.graphql<{\n data?: { testcaseLibraries?: Array<{ uuid: string, name: string, testcaseCaseCount: number }> }\n }>(TESTCASE_LIBRARY_LIST_QUERY, {}, 'library-select')\n\n const libs = libData.data?.testcaseLibraries ?? []\n if (libs.length === 0) {\n throw new Error('ONES: No testcase libraries found for this team')\n }\n // Pick the library with the most cases (most likely the main one)\n libs.sort((a, b) => b.testcaseCaseCount - a.testcaseCaseCount)\n libraryUuid = libs[0].uuid\n }\n\n // Step 1: Search task by number to get task name\n const searchData = await this.graphql<{\n data?: { buckets?: Array<{ tasks?: Array<{ uuid: string, number: number, name: string }> }> }\n }>(\n SEARCH_TASKS_QUERY,\n {\n groupBy: { tasks: {} },\n groupOrderBy: null,\n orderBy: { createTime: 'DESC' },\n filterGroup: [{ number_in: [params.taskNumber] }],\n search: null,\n pagination: { limit: 10, preciseCount: false },\n limit: 10,\n },\n 'group-task-data',\n )\n\n const allTasks = searchData.data?.buckets?.flatMap(b => b.tasks ?? []) ?? []\n const task = allTasks.find(t => t.number === params.taskNumber)\n if (!task) {\n throw new Error(`ONES: Task #${params.taskNumber} not found`)\n }\n\n // Step 2: Search testcase module by task number pattern (e.g. \"#302\")\n const moduleData = await this.graphql<{\n data?: { testcaseModules?: Array<{ uuid: string, name: string }> }\n }>(\n TESTCASE_MODULE_SEARCH_QUERY,\n { filter: { testcaseLibrary_in: [libraryUuid], name_match: `#${params.taskNumber}` } },\n 'find-testcase-module',\n )\n\n const modules = moduleData.data?.testcaseModules ?? []\n if (modules.length === 0) {\n throw new Error(`ONES: No testcase module matching \"#${params.taskNumber}\" in library ${libraryUuid}`)\n }\n const mod = modules[0]\n\n // Step 3: List ALL testcases under this module (paginated)\n const caseList: Array<{ uuid: string, id: string, name: string }> = []\n let cursor = ''\n let totalCount = 0\n\n while (true) {\n const listData = await this.graphql<{\n data?: {\n buckets?: Array<{\n pageInfo: { totalCount: number, hasNextPage: boolean, endCursor: string }\n testcaseCases: Array<{ uuid: string, id: string, name: string }>\n }>\n }\n }>(\n TESTCASE_LIST_PAGED_QUERY,\n {\n testCaseFilter: [{ testcaseLibrary_in: [libraryUuid], path_match: mod.uuid }],\n pagination: { limit: 50, after: cursor, preciseCount: true },\n },\n 'testcase-list-paged',\n )\n\n const bucket = listData.data?.buckets?.[0]\n if (!bucket)\n break\n\n caseList.push(...(bucket.testcaseCases ?? []))\n totalCount = bucket.pageInfo.totalCount\n\n if (!bucket.pageInfo.hasNextPage)\n break\n cursor = bucket.pageInfo.endCursor\n }\n\n if (caseList.length === 0) {\n return { taskNumber: params.taskNumber, taskName: task.name, moduleName: mod.name, moduleUuid: mod.uuid, totalCount: 0, cases: [] }\n }\n\n // Step 4: Fetch details + steps in batches of 20\n const allCases: TestCase[] = []\n const BATCH_SIZE = 20\n for (let i = 0; i < caseList.length; i += BATCH_SIZE) {\n const batch = caseList.slice(i, i + BATCH_SIZE)\n const uuids = batch.map(c => c.uuid)\n\n const detailData = await this.graphql<{\n data?: {\n testcaseCases: Array<{\n uuid: string\n id: string\n name: string\n condition: string\n desc: string\n path: string\n assign?: { name: string } | null\n priority?: { value: string } | null\n type?: { value: string } | null\n }>\n testcaseCaseSteps: Array<{\n uuid: string\n desc: string\n result: string\n index: number\n testcaseCase: { uuid: string }\n }>\n }\n }>(\n TESTCASE_DETAIL_QUERY,\n { testCaseFilter: { uuid_in: [...uuids, null] }, stepFilter: { testcaseCase_in: uuids } },\n 'library-testcase-detail',\n )\n\n const cases = detailData.data?.testcaseCases ?? []\n const steps = detailData.data?.testcaseCaseSteps ?? []\n\n const stepsByCase = new Map<string, TestCaseStep[]>()\n for (const step of steps) {\n const caseUuid = step.testcaseCase.uuid\n if (!stepsByCase.has(caseUuid))\n stepsByCase.set(caseUuid, [])\n stepsByCase.get(caseUuid)!.push({ uuid: step.uuid, index: step.index, desc: step.desc ?? '', result: step.result ?? '' })\n }\n\n for (const c of cases) {\n // Refresh stale image URLs in desc (ONES returns placeholder base64 for lazy-loaded images)\n const freshDesc = c.desc ? await this.refreshImageUrls(c.desc) : ''\n\n allCases.push({\n uuid: c.uuid,\n id: c.id,\n name: c.name,\n priority: c.priority?.value ?? 'N/A',\n type: c.type?.value ?? 'Unknown',\n assignName: c.assign?.name ?? null,\n condition: c.condition ?? '',\n desc: freshDesc,\n steps: (stepsByCase.get(c.uuid) ?? []).sort((a, b) => a.index - b.index),\n modulePath: c.path ?? '',\n })\n }\n }\n\n return { taskNumber: params.taskNumber, taskName: task.name, moduleName: mod.name, moduleUuid: mod.uuid, totalCount, cases: allCases }\n }\n}\n","import type { SourceConfig } from '../types/config.js'\nimport type { SourceType } from '../types/requirement.js'\nimport type { BaseAdapter } from './base.js'\nimport { OnesAdapter } from './ones.js'\n\nconst ADAPTER_MAP: Record<string, new (\n sourceType: SourceType,\n config: SourceConfig,\n resolvedAuth: Record<string, string>,\n) => BaseAdapter> = {\n ones: OnesAdapter,\n}\n\n/**\n * Factory function to create the appropriate adapter based on source type.\n */\nexport function createAdapter(\n sourceType: SourceType,\n config: SourceConfig,\n resolvedAuth: Record<string, string>,\n): BaseAdapter {\n const AdapterClass = ADAPTER_MAP[sourceType]\n if (!AdapterClass) {\n throw new Error(\n `Unsupported source type: \"${sourceType}\". Supported: ${Object.keys(ADAPTER_MAP).join(', ')}`,\n )\n }\n return new AdapterClass(sourceType, config, resolvedAuth)\n}\n\nexport { BaseAdapter } from './base.js'\nexport { OnesAdapter } from './ones.js'\n","import type { AuthConfig } from '../types/auth.js'\nimport type { McpConfig, SourceConfig } from '../types/config.js'\nimport type { SourceType } from '../types/requirement.js'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { z } from 'zod/v4'\n\nconst AuthSchema = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('token'),\n tokenEnv: z.string(),\n }),\n z.object({\n type: z.literal('basic'),\n usernameEnv: z.string(),\n passwordEnv: z.string(),\n }),\n z.object({\n type: z.literal('oauth2'),\n clientIdEnv: z.string(),\n clientSecretEnv: z.string(),\n tokenUrl: z.string().url(),\n }),\n z.object({\n type: z.literal('cookie'),\n cookieEnv: z.string(),\n }),\n z.object({\n type: z.literal('custom'),\n headerName: z.string(),\n valueEnv: z.string(),\n }),\n z.object({\n type: z.literal('ones-pkce'),\n emailEnv: z.string(),\n passwordEnv: z.string(),\n }),\n])\n\nconst SourceConfigSchema = z.object({\n enabled: z.boolean(),\n apiBase: z.string().url(),\n auth: AuthSchema,\n headers: z.record(z.string(), z.string()).optional(),\n options: z.record(z.string(), z.unknown()).optional(),\n})\n\nconst SourcesSchema = z.object({\n ones: SourceConfigSchema.optional(),\n})\n\nconst McpConfigSchema = z.object({\n sources: SourcesSchema,\n defaultSource: z.enum(['ones']).optional(),\n})\n\nconst CONFIG_FILENAME = '.requirements-mcp.json'\n\n/**\n * Search for config file starting from `startDir` and walking up to the root.\n */\nfunction findConfigFile(startDir: string): string | null {\n let dir = resolve(startDir)\n while (true) {\n const candidate = resolve(dir, CONFIG_FILENAME)\n if (existsSync(candidate)) {\n return candidate\n }\n const parent = dirname(dir)\n if (parent === dir)\n break\n dir = parent\n }\n return null\n}\n\n/**\n * Resolve environment variable references in auth config.\n * Reads actual env var values for fields ending with \"Env\".\n */\nfunction resolveAuthEnv(auth: AuthConfig): Record<string, string> {\n const resolved: Record<string, string> = {}\n\n for (const [key, value] of Object.entries(auth)) {\n if (key === 'type')\n continue\n if (key.endsWith('Env') && typeof value === 'string') {\n const envValue = process.env[value]\n if (!envValue) {\n throw new Error(`Environment variable \"${value}\" is not set (required by auth.${key})`)\n }\n // Strip the \"Env\" suffix for the resolved key\n const resolvedKey = key.slice(0, -3)\n resolved[resolvedKey] = envValue\n }\n else if (typeof value === 'string') {\n resolved[key] = value\n }\n }\n\n return resolved\n}\n\nexport interface ResolvedSource {\n type: SourceType\n config: SourceConfig\n resolvedAuth: Record<string, string>\n}\n\nexport interface LoadConfigResult {\n config: McpConfig\n sources: ResolvedSource[]\n configPath: string\n}\n\n/**\n * Try to build config purely from environment variables.\n * Required env vars: ONES_API_BASE, ONES_ACCOUNT, ONES_PASSWORD\n * Returns null if the required env vars are not all present.\n */\nfunction loadConfigFromEnv(): McpConfig | null {\n const apiBase = process.env.ONES_API_BASE\n const account = process.env.ONES_ACCOUNT\n const password = process.env.ONES_PASSWORD\n\n if (!apiBase || !account || !password) {\n return null\n }\n\n // Try to read options from config file if it exists\n let options: Record<string, unknown> | undefined\n const configPath = findConfigFile(process.cwd())\n if (configPath) {\n try {\n const raw = JSON.parse(readFileSync(configPath, 'utf-8')) as { sources?: { ones?: { options?: Record<string, unknown> } } }\n options = raw?.sources?.ones?.options\n }\n catch {\n // ignore parse errors, env config is primary\n }\n }\n\n return {\n sources: {\n ones: {\n enabled: true,\n apiBase,\n auth: {\n type: 'ones-pkce',\n emailEnv: 'ONES_ACCOUNT',\n passwordEnv: 'ONES_PASSWORD',\n },\n options,\n },\n },\n defaultSource: 'ones',\n }\n}\n\n/**\n * Load and validate the MCP config.\n * Priority: env vars (ONES_API_BASE + ONES_ACCOUNT + ONES_PASSWORD) > config file (.requirements-mcp.json).\n * Searches from `startDir` (defaults to cwd) upward for the file.\n */\nexport function loadConfig(startDir?: string): LoadConfigResult {\n // 1. Try environment variables first (simplest setup for MCP)\n const envConfig = loadConfigFromEnv()\n if (envConfig) {\n const sources: ResolvedSource[] = []\n for (const [type, sourceConfig] of Object.entries(envConfig.sources)) {\n if (sourceConfig && sourceConfig.enabled) {\n const resolvedAuth = resolveAuthEnv(sourceConfig.auth)\n sources.push({\n type: type as SourceType,\n config: sourceConfig,\n resolvedAuth,\n })\n }\n }\n return { config: envConfig, sources, configPath: 'env' }\n }\n\n // 2. Fall back to config file\n const dir = startDir ?? process.cwd()\n const configPath = findConfigFile(dir)\n\n if (!configPath) {\n throw new Error(\n `Config not found. Either set env vars (ONES_API_BASE, ONES_ACCOUNT, ONES_PASSWORD) `\n + `or create \"${CONFIG_FILENAME}\" based on .requirements-mcp.json.example`,\n )\n }\n\n const raw = readFileSync(configPath, 'utf-8')\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n }\n catch {\n throw new Error(`Invalid JSON in ${configPath}`)\n }\n\n const result = McpConfigSchema.safeParse(parsed)\n if (!result.success) {\n throw new Error(\n `Invalid config in ${configPath}:\\n${result.error.issues.map(i => ` - ${i.path.join('.')}: ${i.message}`).join('\\n')}`,\n )\n }\n\n const config = result.data as McpConfig\n\n // Resolve enabled sources\n const sources: ResolvedSource[] = []\n for (const [type, sourceConfig] of Object.entries(config.sources)) {\n if (sourceConfig && sourceConfig.enabled) {\n const resolvedAuth = resolveAuthEnv(sourceConfig.auth)\n sources.push({\n type: type as SourceType,\n config: sourceConfig,\n resolvedAuth,\n })\n }\n }\n\n if (sources.length === 0) {\n throw new Error('No enabled sources found in config. Enable at least one source.')\n }\n\n return { config, sources, configPath }\n}\n\nexport { findConfigFile, loadConfigFromEnv, resolveAuthEnv }\n","import type { BaseAdapter } from '../adapters/base.js'\nimport type { IssueDetail } from '../types/requirement.js'\nimport { z } from 'zod/v4'\n\nexport const GetIssueDetailSchema = z.object({\n issueId: z.string().describe('The issue task ID or key (e.g. \"6W9vW3y8J9DO66Pu\" or \"task-6W9vW3y8J9DO66Pu\")'),\n source: z.string().optional().describe('Source to fetch from. If omitted, uses the default source.'),\n})\n\nexport type GetIssueDetailInput = z.infer<typeof GetIssueDetailSchema>\n\n/**\n * Download an image from URL and return as base64 data URI.\n * Returns null if download fails.\n */\nasync function downloadImageAsBase64(url: string): Promise<{ base64: string, mimeType: string } | null> {\n try {\n const res = await fetch(url, { redirect: 'follow' })\n if (!res.ok)\n return null\n\n const contentType = res.headers.get('content-type') ?? 'image/png'\n const mimeType = contentType.split(';')[0].trim()\n const buffer = Buffer.from(await res.arrayBuffer())\n return { base64: buffer.toString('base64'), mimeType }\n }\n catch {\n return null\n }\n}\n\n/**\n * Extract image URLs from HTML string.\n */\nfunction extractImageUrls(html: string): string[] {\n const imgRegex = /<img[^>]+src=\"([^\"]+)\"[^>]*>/g\n return Array.from(html.matchAll(imgRegex), m => m[1])\n .map(url => url.replace(/&amp;/g, '&'))\n}\n\nexport async function handleGetIssueDetail(\n input: GetIssueDetailInput,\n adapters: Map<string, BaseAdapter>,\n defaultSource?: string,\n) {\n const sourceType = input.source ?? defaultSource\n if (!sourceType) {\n throw new Error('No source specified and no default source configured')\n }\n\n const adapter = adapters.get(sourceType)\n if (!adapter) {\n throw new Error(\n `Source \"${sourceType}\" is not configured. Available: ${[...adapters.keys()].join(', ')}`,\n )\n }\n\n const detail = await adapter.getIssueDetail({ issueId: input.issueId })\n\n // Extract and download images from description\n const imageUrls = detail.descriptionRich ? extractImageUrls(detail.descriptionRich) : []\n const imageResults = await Promise.all(imageUrls.map(url => downloadImageAsBase64(url)))\n\n // Build MCP content: text first, then embedded images\n const content: Array<{ type: 'text', text: string } | { type: 'image', data: string, mimeType: string }> = [\n { type: 'text' as const, text: formatIssueDetail(detail) },\n ]\n\n for (let i = 0; i < imageResults.length; i++) {\n const img = imageResults[i]\n if (img) {\n content.push({\n type: 'image' as const,\n data: img.base64,\n mimeType: img.mimeType,\n })\n }\n }\n\n return { content }\n}\n\nfunction formatIssueDetail(detail: IssueDetail): string {\n const lines = [\n `# ${detail.name}`,\n '',\n `- **Key**: ${detail.key}`,\n `- **UUID**: ${detail.uuid}`,\n `- **Type**: ${detail.issueTypeName}`,\n `- **Status**: ${detail.statusName} (${detail.statusCategory})`,\n `- **Priority**: ${detail.priorityValue ?? 'N/A'}`,\n `- **Severity**: ${detail.severityLevel ?? 'N/A'}`,\n `- **Assignee**: ${detail.assignName ?? 'Unassigned'}`,\n `- **Owner**: ${detail.ownerName ?? 'Unknown'}`,\n `- **Solver**: ${detail.solverName ?? 'Unassigned'}`,\n ]\n\n if (detail.projectName)\n lines.push(`- **Project**: ${detail.projectName}`)\n if (detail.sprintName)\n lines.push(`- **Sprint**: ${detail.sprintName}`)\n if (detail.deadline)\n lines.push(`- **Deadline**: ${detail.deadline}`)\n\n lines.push('', '## Description', '')\n if (detail.descriptionRich) {\n lines.push(detail.descriptionRich)\n }\n else if (detail.descriptionText) {\n lines.push(detail.descriptionText)\n }\n else {\n lines.push('_No description_')\n }\n\n return lines.join('\\n')\n}\n","import type { BaseAdapter } from '../adapters/base.js'\nimport type { RelatedIssue } from '../types/requirement.js'\nimport { z } from 'zod/v4'\n\nexport const GetRelatedIssuesSchema = z.object({\n taskId: z.string().describe('The parent task ID or key (e.g. \"HRL2p8rTX4mQ9xMv\" or \"task-HRL2p8rTX4mQ9xMv\")'),\n source: z.string().optional().describe('Source to fetch from. If omitted, uses the default source.'),\n})\n\nexport type GetRelatedIssuesInput = z.infer<typeof GetRelatedIssuesSchema>\n\nexport async function handleGetRelatedIssues(\n input: GetRelatedIssuesInput,\n adapters: Map<string, BaseAdapter>,\n defaultSource?: string,\n) {\n const sourceType = input.source ?? defaultSource\n if (!sourceType) {\n throw new Error('No source specified and no default source configured')\n }\n\n const adapter = adapters.get(sourceType)\n if (!adapter) {\n throw new Error(\n `Source \"${sourceType}\" is not configured. Available: ${[...adapters.keys()].join(', ')}`,\n )\n }\n\n const issues = await adapter.getRelatedIssues({ taskId: input.taskId })\n\n return {\n content: [{ type: 'text' as const, text: formatRelatedIssues(issues) }],\n }\n}\n\nfunction formatRelatedIssues(issues: RelatedIssue[]): string {\n const lines = [\n `Found **${issues.length}** pending defects:`,\n '',\n ]\n\n if (issues.length === 0) {\n lines.push('No pending defects found for this task.')\n return lines.join('\\n')\n }\n\n // Group by assignee name\n const grouped = new Map<string, RelatedIssue[]>()\n for (const issue of issues) {\n const assignee = issue.assignName ?? 'Unassigned'\n if (!grouped.has(assignee))\n grouped.set(assignee, [])\n grouped.get(assignee)!.push(issue)\n }\n\n for (const [assignee, group] of grouped) {\n lines.push(`## ${assignee} (${group.length})`)\n lines.push('')\n for (const issue of group) {\n lines.push(`### ${issue.key}: ${issue.name}`)\n lines.push(`- Status: ${issue.statusName} | Priority: ${issue.priorityValue ?? 'N/A'}`)\n if (issue.projectName) {\n lines.push(`- Project: ${issue.projectName}`)\n }\n lines.push('')\n }\n }\n\n return lines.join('\\n')\n}\n","import type { BaseAdapter } from '../adapters/base.js'\nimport { z } from 'zod/v4'\n\nexport const GetRequirementSchema = z.object({\n id: z.string().describe('The requirement/issue ID'),\n source: z.string().optional().describe('Source to fetch from. If omitted, uses the default source.'),\n})\n\nexport type GetRequirementInput = z.infer<typeof GetRequirementSchema>\n\nexport async function handleGetRequirement(\n input: GetRequirementInput,\n adapters: Map<string, BaseAdapter>,\n defaultSource?: string,\n) {\n const sourceType = input.source ?? defaultSource\n if (!sourceType) {\n throw new Error('No source specified and no default source configured')\n }\n\n const adapter = adapters.get(sourceType)\n if (!adapter) {\n throw new Error(\n `Source \"${sourceType}\" is not configured. Available: ${[...adapters.keys()].join(', ')}`,\n )\n }\n\n const requirement = await adapter.getRequirement({ id: input.id })\n\n return {\n content: [\n {\n type: 'text' as const,\n text: formatRequirement(requirement),\n },\n ],\n }\n}\n\nfunction formatRequirement(req: import('../types/requirement.js').Requirement): string {\n const lines = [\n `# ${req.title}`,\n '',\n `- **ID**: ${req.id}`,\n `- **Source**: ${req.source}`,\n `- **Status**: ${req.status}`,\n `- **Priority**: ${req.priority}`,\n `- **Type**: ${req.type}`,\n `- **Assignee**: ${req.assignee ?? 'Unassigned'}`,\n `- **Reporter**: ${req.reporter || 'Unknown'}`,\n ]\n\n if (req.createdAt) {\n lines.push(`- **Created**: ${req.createdAt}`)\n }\n if (req.updatedAt) {\n lines.push(`- **Updated**: ${req.updatedAt}`)\n }\n\n if (req.dueDate) {\n lines.push(`- **Due**: ${req.dueDate}`)\n }\n\n if (req.labels.length > 0) {\n lines.push(`- **Labels**: ${req.labels.join(', ')}`)\n }\n\n lines.push('', '## Description', '', req.description || '_No description_')\n\n if (req.attachments.length > 0) {\n lines.push('', '## Attachments')\n for (const att of req.attachments) {\n lines.push(`- [${att.name}](${att.url}) (${att.mimeType}, ${att.size} bytes)`)\n }\n }\n\n return lines.join('\\n')\n}\n","import type { BaseAdapter } from '../adapters/base.js'\nimport type { TestCaseResult } from '../types/requirement.js'\nimport { z } from 'zod/v4'\n\nexport const GetTestcasesSchema = z.object({\n taskNumber: z.string().describe('Task number (e.g. \"302\" or \"#302\"). Finds all testcases in the matching module.'),\n libraryUuid: z.string().optional().describe('Testcase library UUID. If omitted, uses configured default.'),\n source: z.string().optional().describe('Source to fetch from. If omitted, uses the default source.'),\n})\n\nexport type GetTestcasesInput = z.infer<typeof GetTestcasesSchema>\n\nexport async function handleGetTestcases(\n input: GetTestcasesInput,\n adapters: Map<string, BaseAdapter>,\n defaultSource?: string,\n) {\n const sourceType = input.source ?? defaultSource\n if (!sourceType) {\n throw new Error('No source specified and no default source configured')\n }\n\n const adapter = adapters.get(sourceType)\n if (!adapter) {\n throw new Error(\n `Source \"${sourceType}\" is not configured. Available: ${[...adapters.keys()].join(', ')}`,\n )\n }\n\n const numMatch = input.taskNumber.match(/^#?(\\d+)$/)\n if (!numMatch) {\n throw new Error(`Invalid task number: \"${input.taskNumber}\". Expected a number like \"302\" or \"#302\".`)\n }\n\n const result = await adapter.getTestcases({\n taskNumber: Number.parseInt(numMatch[1], 10),\n libraryUuid: input.libraryUuid,\n })\n\n return {\n content: [{ type: 'text' as const, text: formatTestcases(result) }],\n }\n}\n\nfunction formatTestcases(result: TestCaseResult): string {\n const lines = [\n `# ${result.taskName} — 测试用例`,\n '',\n `- **模块**: ${result.moduleName}`,\n `- **共 ${result.totalCount} 个用例**(已加载 ${result.cases.length} 个)`,\n '',\n ]\n\n for (const tc of result.cases) {\n lines.push(`## ${tc.id} ${tc.name}`)\n lines.push('')\n lines.push(`- 优先级: ${tc.priority} | 类型: ${tc.type}`)\n if (tc.assignName)\n lines.push(`- 维护人: ${tc.assignName}`)\n if (tc.condition)\n lines.push(`- 前置条件: ${tc.condition}`)\n if (tc.desc)\n lines.push(`- 备注: ${tc.desc}`)\n\n if (tc.steps.length > 0) {\n lines.push('')\n lines.push('| 步骤 | 操作描述 | 预期结果 |')\n lines.push('|------|----------|----------|')\n for (const step of tc.steps) {\n const desc = step.desc.replace(/\\n/g, '<br>')\n const res = step.result.replace(/\\n/g, '<br>')\n lines.push(`| ${step.index + 1} | ${desc} | ${res} |`)\n }\n }\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n","import type { BaseAdapter } from '../adapters/base.js'\nimport type { McpConfig } from '../types/config.js'\n\nexport async function handleListSources(\n adapters: Map<string, BaseAdapter>,\n config: McpConfig,\n) {\n const lines = ['# Configured Sources', '']\n\n if (adapters.size === 0) {\n lines.push('No sources configured.')\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n }\n }\n\n for (const [type, adapter] of adapters) {\n const isDefault = config.defaultSource === type\n const sourceConfig = config.sources[adapter.sourceType]\n lines.push(`## ${type}${isDefault ? ' (default)' : ''}`)\n lines.push(`- **API Base**: ${sourceConfig?.apiBase ?? 'N/A'}`)\n lines.push(`- **Auth Type**: ${sourceConfig?.auth.type ?? 'N/A'}`)\n lines.push('')\n }\n\n if (config.defaultSource) {\n lines.push(`> Default source: **${config.defaultSource}**`)\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n }\n}\n","import type { BaseAdapter } from '../adapters/base.js'\nimport { z } from 'zod/v4'\n\nexport const SearchRequirementsSchema = z.object({\n query: z.string().describe('Search keywords'),\n source: z.string().optional().describe('Source to search. If omitted, searches the default source.'),\n page: z.number().int().min(1).optional().describe('Page number (default: 1)'),\n pageSize: z.number().int().min(1).max(50).optional().describe('Results per page (default: 20, max: 50)'),\n})\n\nexport type SearchRequirementsInput = z.infer<typeof SearchRequirementsSchema>\n\nfunction formatStatusMarker(status: string): string {\n return `[${status.toUpperCase()}]`\n}\n\nexport async function handleSearchRequirements(\n input: SearchRequirementsInput,\n adapters: Map<string, BaseAdapter>,\n defaultSource?: string,\n) {\n const sourceType = input.source ?? defaultSource\n if (!sourceType) {\n throw new Error('No source specified and no default source configured')\n }\n\n const adapter = adapters.get(sourceType)\n if (!adapter) {\n throw new Error(\n `Source \"${sourceType}\" is not configured. Available: ${[...adapters.keys()].join(', ')}`,\n )\n }\n\n const result = await adapter.searchRequirements({\n query: input.query,\n page: input.page,\n pageSize: input.pageSize,\n })\n\n const lines = [\n `Found **${result.total}** items (page ${result.page}/${Math.ceil(result.total / result.pageSize) || 1}):`,\n '',\n ]\n\n if (/\\u6211.*\\u7F3A\\u9677|bug|\\u6211.*\\u4EFB\\u52A1/i.test(input.query)) {\n lines.push(`Query: ${input.query}`)\n lines.push('Use an item ID or number in the next step to fetch detail.')\n lines.push('')\n }\n\n for (const item of result.items) {\n lines.push(`### ${formatStatusMarker(item.status)} ${item.id}: ${item.title}`)\n lines.push(`- Status: ${item.status} | Priority: ${item.priority} | Type: ${item.type}`)\n lines.push(`- Assignee: ${item.assignee ?? 'Unassigned'}`)\n const desc = item.description\n ? (item.description.length > 200 ? `${item.description.slice(0, 200)}...` : item.description)\n : '(empty)'\n lines.push(`- Content: ${desc}`)\n lines.push('')\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: lines.join('\\n'),\n },\n ],\n }\n}\n","import type { BaseAdapter } from './adapters/index.js'\nimport type { LoadConfigResult } from './config/loader.js'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { createAdapter } from './adapters/index.js'\nimport { loadConfig } from './config/loader.js'\nimport { GetIssueDetailSchema, handleGetIssueDetail } from './tools/get-issue-detail.js'\nimport { GetRelatedIssuesSchema, handleGetRelatedIssues } from './tools/get-related-issues.js'\nimport { GetRequirementSchema, handleGetRequirement } from './tools/get-requirement.js'\nimport { GetTestcasesSchema, handleGetTestcases } from './tools/get-testcases.js'\nimport { handleListSources } from './tools/list-sources.js'\nimport { handleSearchRequirements, SearchRequirementsSchema } from './tools/search-requirements.js'\n\n/**\n * Load .env file into process.env (if it exists).\n * Searches from cwd upward, same as config loader.\n */\nfunction loadEnvFile() {\n let dir = process.cwd()\n while (true) {\n const envPath = resolve(dir, '.env')\n if (existsSync(envPath)) {\n const content = readFileSync(envPath, 'utf-8')\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#'))\n continue\n const eqIndex = trimmed.indexOf('=')\n if (eqIndex === -1)\n continue\n const key = trimmed.slice(0, eqIndex).trim()\n let value = trimmed.slice(eqIndex + 1).trim()\n // Strip surrounding quotes\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith('\\'') && value.endsWith('\\''))) {\n value = value.slice(1, -1)\n }\n if (!process.env[key]) {\n process.env[key] = value\n }\n }\n return\n }\n const parent = dirname(dir)\n if (parent === dir)\n break\n dir = parent\n }\n}\n\nasync function main() {\n // Load .env before anything else\n loadEnvFile()\n\n // Load config\n let config: LoadConfigResult\n try {\n config = loadConfig()\n }\n catch (err) {\n console.error(`[requirements-mcp] ${(err as Error).message}`)\n process.exit(1)\n }\n\n // Create adapters for enabled sources\n const adapters = new Map<string, BaseAdapter>()\n for (const source of config.sources) {\n const adapter = createAdapter(source.type, source.config, source.resolvedAuth)\n adapters.set(source.type, adapter)\n }\n\n // Create MCP server\n const server = new McpServer({\n name: 'ai-dev-requirements',\n version: '0.1.0',\n })\n\n // Register tools\n server.tool(\n 'get_requirement',\n 'Fetch a single requirement/issue by its ID from a configured source (ONES)',\n GetRequirementSchema.shape,\n async (params) => {\n try {\n return await handleGetRequirement(params, adapters, config.config.defaultSource)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n server.tool(\n 'search_requirements',\n 'Search for requirements/issues by keywords across a configured source',\n SearchRequirementsSchema.shape,\n async (params) => {\n try {\n return await handleSearchRequirements(params, adapters, config.config.defaultSource)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n server.tool(\n 'list_sources',\n 'List all configured requirement sources and their status',\n {},\n async () => {\n try {\n return await handleListSources(adapters, config.config)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n server.tool(\n 'get_related_issues',\n 'Get pending defect issues (bugs) related to a requirement task. Returns all pending defects grouped by assignee (current user first).',\n GetRelatedIssuesSchema.shape,\n async (params) => {\n try {\n return await handleGetRelatedIssues(params, adapters, config.config.defaultSource)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n server.tool(\n 'get_issue_detail',\n 'Get detailed information about a specific issue/defect including description, rich text, and images',\n GetIssueDetailSchema.shape,\n async (params) => {\n try {\n return await handleGetIssueDetail(params, adapters, config.config.defaultSource)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n server.tool(\n 'get_testcases',\n '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.',\n GetTestcasesSchema.shape,\n async (params) => {\n try {\n return await handleGetTestcases(params, adapters, config.config.defaultSource)\n }\n catch (err) {\n return {\n content: [{ type: 'text', text: `Error: ${(err as Error).message}` }],\n isError: true,\n }\n }\n },\n )\n\n // Start stdio transport\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nmain().catch((err) => {\n console.error('[requirements-mcp] Fatal error:', err)\n process.exit(1)\n})\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-dev-requirements",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "packageManager": "pnpm@10.28.1",
5
5
  "description": "MCP server for fetching requirements from ONES, bundled with a parallel task framework for AI-assisted development",
6
6
  "type": "module",