ai-dev-requirements 0.1.4 → 0.1.6
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 +241 -2
- package/dist/index.mjs +241 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -133,6 +133,60 @@ const SEARCH_TASKS_QUERY = `
|
|
|
133
133
|
}
|
|
134
134
|
`;
|
|
135
135
|
const TASK_BY_NUMBER_QUERY = SEARCH_TASKS_QUERY;
|
|
136
|
+
const RELATED_TASKS_QUERY = `
|
|
137
|
+
query Task($key: Key) {
|
|
138
|
+
task(key: $key) {
|
|
139
|
+
key
|
|
140
|
+
relatedTasks {
|
|
141
|
+
key
|
|
142
|
+
uuid
|
|
143
|
+
name
|
|
144
|
+
path
|
|
145
|
+
deadline
|
|
146
|
+
project { uuid name }
|
|
147
|
+
priority { value }
|
|
148
|
+
issueType {
|
|
149
|
+
key uuid name detailType
|
|
150
|
+
}
|
|
151
|
+
subIssueType {
|
|
152
|
+
key uuid name detailType
|
|
153
|
+
}
|
|
154
|
+
status {
|
|
155
|
+
uuid name category
|
|
156
|
+
}
|
|
157
|
+
assign {
|
|
158
|
+
uuid name
|
|
159
|
+
}
|
|
160
|
+
sprint {
|
|
161
|
+
name uuid
|
|
162
|
+
}
|
|
163
|
+
statusCategory
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
`;
|
|
168
|
+
const ISSUE_DETAIL_QUERY = `
|
|
169
|
+
query Task($key: Key) {
|
|
170
|
+
task(key: $key) {
|
|
171
|
+
key uuid
|
|
172
|
+
description
|
|
173
|
+
descriptionText
|
|
174
|
+
desc_rich: description
|
|
175
|
+
name
|
|
176
|
+
issueType { key uuid name detailType }
|
|
177
|
+
subIssueType { key uuid name detailType }
|
|
178
|
+
status { uuid name category }
|
|
179
|
+
priority { value }
|
|
180
|
+
assign { uuid name }
|
|
181
|
+
owner { uuid name }
|
|
182
|
+
solver { uuid name }
|
|
183
|
+
project { uuid name }
|
|
184
|
+
severityLevel { value }
|
|
185
|
+
deadline(unit: ONESDATE)
|
|
186
|
+
sprint { name uuid }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
`;
|
|
136
190
|
const DEFAULT_STATUS_NOT_IN = [
|
|
137
191
|
"FgMGkcaq",
|
|
138
192
|
"NvRwHBSo",
|
|
@@ -301,6 +355,7 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
301
355
|
accessToken: token.access_token,
|
|
302
356
|
teamUuid,
|
|
303
357
|
orgUuid: orgUser.org_uuid,
|
|
358
|
+
userUuid: orgUser.org_user.org_user_uuid,
|
|
304
359
|
expiresAt: Date.now() + (token.expires_in - 60) * 1e3
|
|
305
360
|
};
|
|
306
361
|
return this.session;
|
|
@@ -397,7 +452,10 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
397
452
|
if (task.relatedTasks?.length) {
|
|
398
453
|
parts.push("");
|
|
399
454
|
parts.push("## Related Tasks");
|
|
400
|
-
for (const related of task.relatedTasks)
|
|
455
|
+
for (const related of task.relatedTasks) {
|
|
456
|
+
const assignee = related.assign?.name ?? "Unassigned";
|
|
457
|
+
parts.push(`- #${related.number} ${related.name} [${related.issueType?.name}] (${related.status?.name}) — ${assignee}`);
|
|
458
|
+
}
|
|
401
459
|
}
|
|
402
460
|
if (task.parent?.uuid) {
|
|
403
461
|
parts.push("");
|
|
@@ -461,6 +519,74 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
461
519
|
pageSize
|
|
462
520
|
};
|
|
463
521
|
}
|
|
522
|
+
async getRelatedIssues(params) {
|
|
523
|
+
const session = await this.login();
|
|
524
|
+
const taskKey = params.taskId.startsWith("task-") ? params.taskId : `task-${params.taskId}`;
|
|
525
|
+
const filtered = ((await this.graphql(RELATED_TASKS_QUERY, { key: taskKey }, "Task")).data?.task?.relatedTasks ?? []).filter((t) => {
|
|
526
|
+
const isDefect = t.issueType?.detailType === 3 || t.subIssueType?.detailType === 3;
|
|
527
|
+
const isTodo = t.status?.category === "to_do";
|
|
528
|
+
return isDefect && isTodo;
|
|
529
|
+
});
|
|
530
|
+
const currentUserUuid = session.userUuid;
|
|
531
|
+
filtered.sort((a, b) => {
|
|
532
|
+
return (a.assign?.uuid === currentUserUuid ? 0 : 1) - (b.assign?.uuid === currentUserUuid ? 0 : 1);
|
|
533
|
+
});
|
|
534
|
+
return filtered.map((t) => ({
|
|
535
|
+
key: t.key,
|
|
536
|
+
uuid: t.uuid,
|
|
537
|
+
name: t.name,
|
|
538
|
+
issueTypeName: t.issueType?.name ?? "Unknown",
|
|
539
|
+
statusName: t.status?.name ?? "Unknown",
|
|
540
|
+
statusCategory: t.status?.category ?? "unknown",
|
|
541
|
+
assignName: t.assign?.name ?? null,
|
|
542
|
+
assignUuid: t.assign?.uuid ?? null,
|
|
543
|
+
priorityValue: t.priority?.value ?? null,
|
|
544
|
+
projectName: t.project?.name ?? null
|
|
545
|
+
}));
|
|
546
|
+
}
|
|
547
|
+
async getIssueDetail(params) {
|
|
548
|
+
let issueKey;
|
|
549
|
+
const numMatch = params.issueId.match(/^#?(\d+)$/);
|
|
550
|
+
if (numMatch) {
|
|
551
|
+
const taskNumber = Number.parseInt(numMatch[1], 10);
|
|
552
|
+
const found = ((await this.graphql(SEARCH_TASKS_QUERY, {
|
|
553
|
+
groupBy: { tasks: {} },
|
|
554
|
+
groupOrderBy: null,
|
|
555
|
+
orderBy: { createTime: "DESC" },
|
|
556
|
+
filterGroup: [{ number_in: [taskNumber] }],
|
|
557
|
+
search: null,
|
|
558
|
+
pagination: {
|
|
559
|
+
limit: 10,
|
|
560
|
+
preciseCount: false
|
|
561
|
+
},
|
|
562
|
+
limit: 10
|
|
563
|
+
}, "group-task-data")).data?.buckets?.flatMap((b) => b.tasks ?? []) ?? []).find((t) => t.number === taskNumber);
|
|
564
|
+
if (!found) throw new Error(`ONES: Issue #${taskNumber} not found in current team`);
|
|
565
|
+
issueKey = `task-${found.uuid}`;
|
|
566
|
+
} else issueKey = params.issueId.startsWith("task-") ? params.issueId : `task-${params.issueId}`;
|
|
567
|
+
const task = (await this.graphql(ISSUE_DETAIL_QUERY, { key: issueKey }, "Task")).data?.task;
|
|
568
|
+
if (!task) throw new Error(`ONES: Issue "${issueKey}" not found`);
|
|
569
|
+
return {
|
|
570
|
+
key: task.key,
|
|
571
|
+
uuid: task.uuid,
|
|
572
|
+
name: task.name,
|
|
573
|
+
description: task.description ?? "",
|
|
574
|
+
descriptionRich: task.desc_rich ?? "",
|
|
575
|
+
descriptionText: task.descriptionText ?? "",
|
|
576
|
+
issueTypeName: task.issueType?.name ?? "Unknown",
|
|
577
|
+
statusName: task.status?.name ?? "Unknown",
|
|
578
|
+
statusCategory: task.status?.category ?? "unknown",
|
|
579
|
+
assignName: task.assign?.name ?? null,
|
|
580
|
+
ownerName: task.owner?.name ?? null,
|
|
581
|
+
solverName: task.solver?.name ?? null,
|
|
582
|
+
priorityValue: task.priority?.value ?? null,
|
|
583
|
+
severityLevel: task.severityLevel?.value ?? null,
|
|
584
|
+
projectName: task.project?.name ?? null,
|
|
585
|
+
deadline: task.deadline ?? null,
|
|
586
|
+
sprintName: task.sprint?.name ?? null,
|
|
587
|
+
raw: task
|
|
588
|
+
};
|
|
589
|
+
}
|
|
464
590
|
};
|
|
465
591
|
|
|
466
592
|
//#endregion
|
|
@@ -627,6 +753,93 @@ function loadConfig(startDir) {
|
|
|
627
753
|
};
|
|
628
754
|
}
|
|
629
755
|
|
|
756
|
+
//#endregion
|
|
757
|
+
//#region src/tools/get-issue-detail.ts
|
|
758
|
+
const GetIssueDetailSchema = zod_v4.z.object({
|
|
759
|
+
issueId: zod_v4.z.string().describe("The issue task ID or key (e.g. \"6W9vW3y8J9DO66Pu\" or \"task-6W9vW3y8J9DO66Pu\")"),
|
|
760
|
+
source: zod_v4.z.string().optional().describe("Source to fetch from. If omitted, uses the default source.")
|
|
761
|
+
});
|
|
762
|
+
async function handleGetIssueDetail(input, adapters, defaultSource) {
|
|
763
|
+
const sourceType = input.source ?? defaultSource;
|
|
764
|
+
if (!sourceType) throw new Error("No source specified and no default source configured");
|
|
765
|
+
const adapter = adapters.get(sourceType);
|
|
766
|
+
if (!adapter) throw new Error(`Source "${sourceType}" is not configured. Available: ${[...adapters.keys()].join(", ")}`);
|
|
767
|
+
return { content: [{
|
|
768
|
+
type: "text",
|
|
769
|
+
text: formatIssueDetail(await adapter.getIssueDetail({ issueId: input.issueId }))
|
|
770
|
+
}] };
|
|
771
|
+
}
|
|
772
|
+
function formatIssueDetail(detail) {
|
|
773
|
+
const lines = [
|
|
774
|
+
`# ${detail.name}`,
|
|
775
|
+
"",
|
|
776
|
+
`- **Key**: ${detail.key}`,
|
|
777
|
+
`- **UUID**: ${detail.uuid}`,
|
|
778
|
+
`- **Type**: ${detail.issueTypeName}`,
|
|
779
|
+
`- **Status**: ${detail.statusName} (${detail.statusCategory})`,
|
|
780
|
+
`- **Priority**: ${detail.priorityValue ?? "N/A"}`,
|
|
781
|
+
`- **Severity**: ${detail.severityLevel ?? "N/A"}`,
|
|
782
|
+
`- **Assignee**: ${detail.assignName ?? "Unassigned"}`,
|
|
783
|
+
`- **Owner**: ${detail.ownerName ?? "Unknown"}`,
|
|
784
|
+
`- **Solver**: ${detail.solverName ?? "Unassigned"}`
|
|
785
|
+
];
|
|
786
|
+
if (detail.projectName) lines.push(`- **Project**: ${detail.projectName}`);
|
|
787
|
+
if (detail.sprintName) lines.push(`- **Sprint**: ${detail.sprintName}`);
|
|
788
|
+
if (detail.deadline) lines.push(`- **Deadline**: ${detail.deadline}`);
|
|
789
|
+
lines.push("", "## Description", "");
|
|
790
|
+
if (detail.descriptionRich) {
|
|
791
|
+
lines.push(detail.descriptionRich);
|
|
792
|
+
const images = Array.from(detail.descriptionRich.matchAll(/<img[^>]+src="([^"]+)"[^>]*>/g), (m) => m[1]);
|
|
793
|
+
if (images.length > 0) {
|
|
794
|
+
lines.push("", "## Images", "");
|
|
795
|
+
for (const url of images) lines.push(`- `);
|
|
796
|
+
}
|
|
797
|
+
} else if (detail.descriptionText) lines.push(detail.descriptionText);
|
|
798
|
+
else lines.push("_No description_");
|
|
799
|
+
return lines.join("\n");
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
//#endregion
|
|
803
|
+
//#region src/tools/get-related-issues.ts
|
|
804
|
+
const GetRelatedIssuesSchema = zod_v4.z.object({
|
|
805
|
+
taskId: zod_v4.z.string().describe("The parent task ID or key (e.g. \"HRL2p8rTX4mQ9xMv\" or \"task-HRL2p8rTX4mQ9xMv\")"),
|
|
806
|
+
source: zod_v4.z.string().optional().describe("Source to fetch from. If omitted, uses the default source.")
|
|
807
|
+
});
|
|
808
|
+
async function handleGetRelatedIssues(input, adapters, defaultSource) {
|
|
809
|
+
const sourceType = input.source ?? defaultSource;
|
|
810
|
+
if (!sourceType) throw new Error("No source specified and no default source configured");
|
|
811
|
+
const adapter = adapters.get(sourceType);
|
|
812
|
+
if (!adapter) throw new Error(`Source "${sourceType}" is not configured. Available: ${[...adapters.keys()].join(", ")}`);
|
|
813
|
+
return { content: [{
|
|
814
|
+
type: "text",
|
|
815
|
+
text: formatRelatedIssues(await adapter.getRelatedIssues({ taskId: input.taskId }))
|
|
816
|
+
}] };
|
|
817
|
+
}
|
|
818
|
+
function formatRelatedIssues(issues) {
|
|
819
|
+
const lines = [`Found **${issues.length}** pending defects:`, ""];
|
|
820
|
+
if (issues.length === 0) {
|
|
821
|
+
lines.push("No pending defects found for this task.");
|
|
822
|
+
return lines.join("\n");
|
|
823
|
+
}
|
|
824
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
825
|
+
for (const issue of issues) {
|
|
826
|
+
const assignee = issue.assignName ?? "Unassigned";
|
|
827
|
+
if (!grouped.has(assignee)) grouped.set(assignee, []);
|
|
828
|
+
grouped.get(assignee).push(issue);
|
|
829
|
+
}
|
|
830
|
+
for (const [assignee, group] of grouped) {
|
|
831
|
+
lines.push(`## ${assignee} (${group.length})`);
|
|
832
|
+
lines.push("");
|
|
833
|
+
for (const issue of group) {
|
|
834
|
+
lines.push(`### ${issue.key}: ${issue.name}`);
|
|
835
|
+
lines.push(`- Status: ${issue.statusName} | Priority: ${issue.priorityValue ?? "N/A"}`);
|
|
836
|
+
if (issue.projectName) lines.push(`- Project: ${issue.projectName}`);
|
|
837
|
+
lines.push("");
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return lines.join("\n");
|
|
841
|
+
}
|
|
842
|
+
|
|
630
843
|
//#endregion
|
|
631
844
|
//#region src/tools/get-requirement.ts
|
|
632
845
|
const GetRequirementSchema = zod_v4.z.object({
|
|
@@ -814,6 +1027,32 @@ async function main() {
|
|
|
814
1027
|
};
|
|
815
1028
|
}
|
|
816
1029
|
});
|
|
1030
|
+
server.tool("get_related_issues", "Get pending defect issues (bugs) related to a requirement task. Returns all pending defects grouped by assignee (current user first).", GetRelatedIssuesSchema.shape, async (params) => {
|
|
1031
|
+
try {
|
|
1032
|
+
return await handleGetRelatedIssues(params, adapters, config.config.defaultSource);
|
|
1033
|
+
} catch (err) {
|
|
1034
|
+
return {
|
|
1035
|
+
content: [{
|
|
1036
|
+
type: "text",
|
|
1037
|
+
text: `Error: ${err.message}`
|
|
1038
|
+
}],
|
|
1039
|
+
isError: true
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
server.tool("get_issue_detail", "Get detailed information about a specific issue/defect including description, rich text, and images", GetIssueDetailSchema.shape, async (params) => {
|
|
1044
|
+
try {
|
|
1045
|
+
return await handleGetIssueDetail(params, adapters, config.config.defaultSource);
|
|
1046
|
+
} catch (err) {
|
|
1047
|
+
return {
|
|
1048
|
+
content: [{
|
|
1049
|
+
type: "text",
|
|
1050
|
+
text: `Error: ${err.message}`
|
|
1051
|
+
}],
|
|
1052
|
+
isError: true
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
817
1056
|
const transport = new _modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport();
|
|
818
1057
|
await server.connect(transport);
|
|
819
1058
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -105,6 +105,60 @@ const SEARCH_TASKS_QUERY = `
|
|
|
105
105
|
}
|
|
106
106
|
`;
|
|
107
107
|
const TASK_BY_NUMBER_QUERY = SEARCH_TASKS_QUERY;
|
|
108
|
+
const RELATED_TASKS_QUERY = `
|
|
109
|
+
query Task($key: Key) {
|
|
110
|
+
task(key: $key) {
|
|
111
|
+
key
|
|
112
|
+
relatedTasks {
|
|
113
|
+
key
|
|
114
|
+
uuid
|
|
115
|
+
name
|
|
116
|
+
path
|
|
117
|
+
deadline
|
|
118
|
+
project { uuid name }
|
|
119
|
+
priority { value }
|
|
120
|
+
issueType {
|
|
121
|
+
key uuid name detailType
|
|
122
|
+
}
|
|
123
|
+
subIssueType {
|
|
124
|
+
key uuid name detailType
|
|
125
|
+
}
|
|
126
|
+
status {
|
|
127
|
+
uuid name category
|
|
128
|
+
}
|
|
129
|
+
assign {
|
|
130
|
+
uuid name
|
|
131
|
+
}
|
|
132
|
+
sprint {
|
|
133
|
+
name uuid
|
|
134
|
+
}
|
|
135
|
+
statusCategory
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
const ISSUE_DETAIL_QUERY = `
|
|
141
|
+
query Task($key: Key) {
|
|
142
|
+
task(key: $key) {
|
|
143
|
+
key uuid
|
|
144
|
+
description
|
|
145
|
+
descriptionText
|
|
146
|
+
desc_rich: description
|
|
147
|
+
name
|
|
148
|
+
issueType { key uuid name detailType }
|
|
149
|
+
subIssueType { key uuid name detailType }
|
|
150
|
+
status { uuid name category }
|
|
151
|
+
priority { value }
|
|
152
|
+
assign { uuid name }
|
|
153
|
+
owner { uuid name }
|
|
154
|
+
solver { uuid name }
|
|
155
|
+
project { uuid name }
|
|
156
|
+
severityLevel { value }
|
|
157
|
+
deadline(unit: ONESDATE)
|
|
158
|
+
sprint { name uuid }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
108
162
|
const DEFAULT_STATUS_NOT_IN = [
|
|
109
163
|
"FgMGkcaq",
|
|
110
164
|
"NvRwHBSo",
|
|
@@ -273,6 +327,7 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
273
327
|
accessToken: token.access_token,
|
|
274
328
|
teamUuid,
|
|
275
329
|
orgUuid: orgUser.org_uuid,
|
|
330
|
+
userUuid: orgUser.org_user.org_user_uuid,
|
|
276
331
|
expiresAt: Date.now() + (token.expires_in - 60) * 1e3
|
|
277
332
|
};
|
|
278
333
|
return this.session;
|
|
@@ -369,7 +424,10 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
369
424
|
if (task.relatedTasks?.length) {
|
|
370
425
|
parts.push("");
|
|
371
426
|
parts.push("## Related Tasks");
|
|
372
|
-
for (const related of task.relatedTasks)
|
|
427
|
+
for (const related of task.relatedTasks) {
|
|
428
|
+
const assignee = related.assign?.name ?? "Unassigned";
|
|
429
|
+
parts.push(`- #${related.number} ${related.name} [${related.issueType?.name}] (${related.status?.name}) — ${assignee}`);
|
|
430
|
+
}
|
|
373
431
|
}
|
|
374
432
|
if (task.parent?.uuid) {
|
|
375
433
|
parts.push("");
|
|
@@ -433,6 +491,74 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
433
491
|
pageSize
|
|
434
492
|
};
|
|
435
493
|
}
|
|
494
|
+
async getRelatedIssues(params) {
|
|
495
|
+
const session = await this.login();
|
|
496
|
+
const taskKey = params.taskId.startsWith("task-") ? params.taskId : `task-${params.taskId}`;
|
|
497
|
+
const filtered = ((await this.graphql(RELATED_TASKS_QUERY, { key: taskKey }, "Task")).data?.task?.relatedTasks ?? []).filter((t) => {
|
|
498
|
+
const isDefect = t.issueType?.detailType === 3 || t.subIssueType?.detailType === 3;
|
|
499
|
+
const isTodo = t.status?.category === "to_do";
|
|
500
|
+
return isDefect && isTodo;
|
|
501
|
+
});
|
|
502
|
+
const currentUserUuid = session.userUuid;
|
|
503
|
+
filtered.sort((a, b) => {
|
|
504
|
+
return (a.assign?.uuid === currentUserUuid ? 0 : 1) - (b.assign?.uuid === currentUserUuid ? 0 : 1);
|
|
505
|
+
});
|
|
506
|
+
return filtered.map((t) => ({
|
|
507
|
+
key: t.key,
|
|
508
|
+
uuid: t.uuid,
|
|
509
|
+
name: t.name,
|
|
510
|
+
issueTypeName: t.issueType?.name ?? "Unknown",
|
|
511
|
+
statusName: t.status?.name ?? "Unknown",
|
|
512
|
+
statusCategory: t.status?.category ?? "unknown",
|
|
513
|
+
assignName: t.assign?.name ?? null,
|
|
514
|
+
assignUuid: t.assign?.uuid ?? null,
|
|
515
|
+
priorityValue: t.priority?.value ?? null,
|
|
516
|
+
projectName: t.project?.name ?? null
|
|
517
|
+
}));
|
|
518
|
+
}
|
|
519
|
+
async getIssueDetail(params) {
|
|
520
|
+
let issueKey;
|
|
521
|
+
const numMatch = params.issueId.match(/^#?(\d+)$/);
|
|
522
|
+
if (numMatch) {
|
|
523
|
+
const taskNumber = Number.parseInt(numMatch[1], 10);
|
|
524
|
+
const found = ((await this.graphql(SEARCH_TASKS_QUERY, {
|
|
525
|
+
groupBy: { tasks: {} },
|
|
526
|
+
groupOrderBy: null,
|
|
527
|
+
orderBy: { createTime: "DESC" },
|
|
528
|
+
filterGroup: [{ number_in: [taskNumber] }],
|
|
529
|
+
search: null,
|
|
530
|
+
pagination: {
|
|
531
|
+
limit: 10,
|
|
532
|
+
preciseCount: false
|
|
533
|
+
},
|
|
534
|
+
limit: 10
|
|
535
|
+
}, "group-task-data")).data?.buckets?.flatMap((b) => b.tasks ?? []) ?? []).find((t) => t.number === taskNumber);
|
|
536
|
+
if (!found) throw new Error(`ONES: Issue #${taskNumber} not found in current team`);
|
|
537
|
+
issueKey = `task-${found.uuid}`;
|
|
538
|
+
} else issueKey = params.issueId.startsWith("task-") ? params.issueId : `task-${params.issueId}`;
|
|
539
|
+
const task = (await this.graphql(ISSUE_DETAIL_QUERY, { key: issueKey }, "Task")).data?.task;
|
|
540
|
+
if (!task) throw new Error(`ONES: Issue "${issueKey}" not found`);
|
|
541
|
+
return {
|
|
542
|
+
key: task.key,
|
|
543
|
+
uuid: task.uuid,
|
|
544
|
+
name: task.name,
|
|
545
|
+
description: task.description ?? "",
|
|
546
|
+
descriptionRich: task.desc_rich ?? "",
|
|
547
|
+
descriptionText: task.descriptionText ?? "",
|
|
548
|
+
issueTypeName: task.issueType?.name ?? "Unknown",
|
|
549
|
+
statusName: task.status?.name ?? "Unknown",
|
|
550
|
+
statusCategory: task.status?.category ?? "unknown",
|
|
551
|
+
assignName: task.assign?.name ?? null,
|
|
552
|
+
ownerName: task.owner?.name ?? null,
|
|
553
|
+
solverName: task.solver?.name ?? null,
|
|
554
|
+
priorityValue: task.priority?.value ?? null,
|
|
555
|
+
severityLevel: task.severityLevel?.value ?? null,
|
|
556
|
+
projectName: task.project?.name ?? null,
|
|
557
|
+
deadline: task.deadline ?? null,
|
|
558
|
+
sprintName: task.sprint?.name ?? null,
|
|
559
|
+
raw: task
|
|
560
|
+
};
|
|
561
|
+
}
|
|
436
562
|
};
|
|
437
563
|
|
|
438
564
|
//#endregion
|
|
@@ -599,6 +725,93 @@ function loadConfig(startDir) {
|
|
|
599
725
|
};
|
|
600
726
|
}
|
|
601
727
|
|
|
728
|
+
//#endregion
|
|
729
|
+
//#region src/tools/get-issue-detail.ts
|
|
730
|
+
const GetIssueDetailSchema = z.object({
|
|
731
|
+
issueId: z.string().describe("The issue task ID or key (e.g. \"6W9vW3y8J9DO66Pu\" or \"task-6W9vW3y8J9DO66Pu\")"),
|
|
732
|
+
source: z.string().optional().describe("Source to fetch from. If omitted, uses the default source.")
|
|
733
|
+
});
|
|
734
|
+
async function handleGetIssueDetail(input, adapters, defaultSource) {
|
|
735
|
+
const sourceType = input.source ?? defaultSource;
|
|
736
|
+
if (!sourceType) throw new Error("No source specified and no default source configured");
|
|
737
|
+
const adapter = adapters.get(sourceType);
|
|
738
|
+
if (!adapter) throw new Error(`Source "${sourceType}" is not configured. Available: ${[...adapters.keys()].join(", ")}`);
|
|
739
|
+
return { content: [{
|
|
740
|
+
type: "text",
|
|
741
|
+
text: formatIssueDetail(await adapter.getIssueDetail({ issueId: input.issueId }))
|
|
742
|
+
}] };
|
|
743
|
+
}
|
|
744
|
+
function formatIssueDetail(detail) {
|
|
745
|
+
const lines = [
|
|
746
|
+
`# ${detail.name}`,
|
|
747
|
+
"",
|
|
748
|
+
`- **Key**: ${detail.key}`,
|
|
749
|
+
`- **UUID**: ${detail.uuid}`,
|
|
750
|
+
`- **Type**: ${detail.issueTypeName}`,
|
|
751
|
+
`- **Status**: ${detail.statusName} (${detail.statusCategory})`,
|
|
752
|
+
`- **Priority**: ${detail.priorityValue ?? "N/A"}`,
|
|
753
|
+
`- **Severity**: ${detail.severityLevel ?? "N/A"}`,
|
|
754
|
+
`- **Assignee**: ${detail.assignName ?? "Unassigned"}`,
|
|
755
|
+
`- **Owner**: ${detail.ownerName ?? "Unknown"}`,
|
|
756
|
+
`- **Solver**: ${detail.solverName ?? "Unassigned"}`
|
|
757
|
+
];
|
|
758
|
+
if (detail.projectName) lines.push(`- **Project**: ${detail.projectName}`);
|
|
759
|
+
if (detail.sprintName) lines.push(`- **Sprint**: ${detail.sprintName}`);
|
|
760
|
+
if (detail.deadline) lines.push(`- **Deadline**: ${detail.deadline}`);
|
|
761
|
+
lines.push("", "## Description", "");
|
|
762
|
+
if (detail.descriptionRich) {
|
|
763
|
+
lines.push(detail.descriptionRich);
|
|
764
|
+
const images = Array.from(detail.descriptionRich.matchAll(/<img[^>]+src="([^"]+)"[^>]*>/g), (m) => m[1]);
|
|
765
|
+
if (images.length > 0) {
|
|
766
|
+
lines.push("", "## Images", "");
|
|
767
|
+
for (const url of images) lines.push(`- `);
|
|
768
|
+
}
|
|
769
|
+
} else if (detail.descriptionText) lines.push(detail.descriptionText);
|
|
770
|
+
else lines.push("_No description_");
|
|
771
|
+
return lines.join("\n");
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
//#endregion
|
|
775
|
+
//#region src/tools/get-related-issues.ts
|
|
776
|
+
const GetRelatedIssuesSchema = z.object({
|
|
777
|
+
taskId: z.string().describe("The parent task ID or key (e.g. \"HRL2p8rTX4mQ9xMv\" or \"task-HRL2p8rTX4mQ9xMv\")"),
|
|
778
|
+
source: z.string().optional().describe("Source to fetch from. If omitted, uses the default source.")
|
|
779
|
+
});
|
|
780
|
+
async function handleGetRelatedIssues(input, adapters, defaultSource) {
|
|
781
|
+
const sourceType = input.source ?? defaultSource;
|
|
782
|
+
if (!sourceType) throw new Error("No source specified and no default source configured");
|
|
783
|
+
const adapter = adapters.get(sourceType);
|
|
784
|
+
if (!adapter) throw new Error(`Source "${sourceType}" is not configured. Available: ${[...adapters.keys()].join(", ")}`);
|
|
785
|
+
return { content: [{
|
|
786
|
+
type: "text",
|
|
787
|
+
text: formatRelatedIssues(await adapter.getRelatedIssues({ taskId: input.taskId }))
|
|
788
|
+
}] };
|
|
789
|
+
}
|
|
790
|
+
function formatRelatedIssues(issues) {
|
|
791
|
+
const lines = [`Found **${issues.length}** pending defects:`, ""];
|
|
792
|
+
if (issues.length === 0) {
|
|
793
|
+
lines.push("No pending defects found for this task.");
|
|
794
|
+
return lines.join("\n");
|
|
795
|
+
}
|
|
796
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
797
|
+
for (const issue of issues) {
|
|
798
|
+
const assignee = issue.assignName ?? "Unassigned";
|
|
799
|
+
if (!grouped.has(assignee)) grouped.set(assignee, []);
|
|
800
|
+
grouped.get(assignee).push(issue);
|
|
801
|
+
}
|
|
802
|
+
for (const [assignee, group] of grouped) {
|
|
803
|
+
lines.push(`## ${assignee} (${group.length})`);
|
|
804
|
+
lines.push("");
|
|
805
|
+
for (const issue of group) {
|
|
806
|
+
lines.push(`### ${issue.key}: ${issue.name}`);
|
|
807
|
+
lines.push(`- Status: ${issue.statusName} | Priority: ${issue.priorityValue ?? "N/A"}`);
|
|
808
|
+
if (issue.projectName) lines.push(`- Project: ${issue.projectName}`);
|
|
809
|
+
lines.push("");
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return lines.join("\n");
|
|
813
|
+
}
|
|
814
|
+
|
|
602
815
|
//#endregion
|
|
603
816
|
//#region src/tools/get-requirement.ts
|
|
604
817
|
const GetRequirementSchema = z.object({
|
|
@@ -786,6 +999,32 @@ async function main() {
|
|
|
786
999
|
};
|
|
787
1000
|
}
|
|
788
1001
|
});
|
|
1002
|
+
server.tool("get_related_issues", "Get pending defect issues (bugs) related to a requirement task. Returns all pending defects grouped by assignee (current user first).", GetRelatedIssuesSchema.shape, async (params) => {
|
|
1003
|
+
try {
|
|
1004
|
+
return await handleGetRelatedIssues(params, adapters, config.config.defaultSource);
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
return {
|
|
1007
|
+
content: [{
|
|
1008
|
+
type: "text",
|
|
1009
|
+
text: `Error: ${err.message}`
|
|
1010
|
+
}],
|
|
1011
|
+
isError: true
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
server.tool("get_issue_detail", "Get detailed information about a specific issue/defect including description, rich text, and images", GetIssueDetailSchema.shape, async (params) => {
|
|
1016
|
+
try {
|
|
1017
|
+
return await handleGetIssueDetail(params, adapters, config.config.defaultSource);
|
|
1018
|
+
} catch (err) {
|
|
1019
|
+
return {
|
|
1020
|
+
content: [{
|
|
1021
|
+
type: "text",
|
|
1022
|
+
text: `Error: ${err.message}`
|
|
1023
|
+
}],
|
|
1024
|
+
isError: true
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
789
1028
|
const transport = new StdioServerTransport();
|
|
790
1029
|
await server.connect(transport);
|
|
791
1030
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -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-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 { 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\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","import type { SourceConfig } from '../types/config.js'\nimport type { Requirement, SearchResult, SourceType } from '../types/requirement.js'\n\nimport type { 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 }\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 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 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 }\n status { uuid name category }\n priority { value }\n assign { uuid name }\n project { uuid name }\n }\n }\n }\n`\n\n// Query to find a task by its number\nconst TASK_BY_NUMBER_QUERY = SEARCH_TASKS_QUERY\nconst DEFAULT_STATUS_NOT_IN = ['FgMGkcaq', 'NvRwHBSo', 'Dn3k8ffK', 'TbmY2So5']\n\n// ============ Helpers ============\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\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 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 /**\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 * 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 parts.push(`- #${related.number} ${related.name} [${related.issueType?.name}] (${related.status?.name})`)\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\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: [\n {\n assign_in: ['${currentUser}'],\n status_notIn: DEFAULT_STATUS_NOT_IN,\n },\n ],\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 // Local keyword filter (matching ones-api.ts behavior)\n if (params.query) {\n const keyword = params.query\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","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 { 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\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}** results (page ${result.page}/${Math.ceil(result.total / result.pageSize) || 1}):`,\n '',\n ]\n\n for (const item of result.items) {\n lines.push(`### ${item.id}: ${item.title}`)\n lines.push(`- Status: ${item.status} | Priority: ${item.priority} | Type: ${item.type}`)\n lines.push(`- Assignee: ${item.assignee ?? 'Unassigned'}`)\n if (item.description) {\n const desc = item.description.length > 200\n ? `${item.description.slice(0, 200)}...`\n : item.description\n lines.push(`- ${desc}`)\n }\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 { 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 // 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/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 }\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 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 }\n status { uuid name category }\n priority { value }\n assign { uuid name }\n project { uuid name }\n }\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 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\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 /**\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 * 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\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: [\n {\n assign_in: ['${currentUser}'],\n status_notIn: DEFAULT_STATUS_NOT_IN,\n },\n ],\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 // Local keyword filter (matching ones-api.ts behavior)\n if (params.query) {\n const keyword = params.query\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 return {\n key: task.key,\n uuid: task.uuid,\n name: task.name,\n description: task.description ?? '',\n descriptionRich: task.desc_rich ?? '',\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\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 return {\n content: [{ type: 'text' as const, text: formatIssueDetail(detail) }],\n }\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 // Extract image URLs from rich text\n const imgRegex = /<img[^>]+src=\"([^\"]+)\"[^>]*>/g\n const images = Array.from(detail.descriptionRich.matchAll(imgRegex), m => m[1])\n if (images.length > 0) {\n lines.push('', '## Images', '')\n for (const url of images) {\n lines.push(`- `)\n }\n }\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\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}** results (page ${result.page}/${Math.ceil(result.total / result.pageSize) || 1}):`,\n '',\n ]\n\n for (const item of result.items) {\n lines.push(`### ${item.id}: ${item.title}`)\n lines.push(`- Status: ${item.status} | Priority: ${item.priority} | Type: ${item.type}`)\n lines.push(`- Assignee: ${item.assignee ?? 'Unassigned'}`)\n if (item.description) {\n const desc = item.description.length > 200\n ? `${item.description.slice(0, 200)}...`\n : item.description\n lines.push(`- ${desc}`)\n }\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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-dev-requirements",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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",
|