ai-dev-requirements 0.1.11 → 0.1.13
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 +591 -90
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +585 -78
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -11
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,6 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
import crypto from "node:crypto";
|
|
7
7
|
import { z } from "zod/v4";
|
|
8
|
-
|
|
9
8
|
//#region src/utils/map-status.ts
|
|
10
9
|
const ONES_STATUS_MAP = {
|
|
11
10
|
to_do: "open",
|
|
@@ -41,7 +40,6 @@ function mapOnesPriority(priority) {
|
|
|
41
40
|
function mapOnesType(type) {
|
|
42
41
|
return ONES_TYPE_MAP[type.toLowerCase()] ?? "task";
|
|
43
42
|
}
|
|
44
|
-
|
|
45
43
|
//#endregion
|
|
46
44
|
//#region src/adapters/base.ts
|
|
47
45
|
/**
|
|
@@ -58,7 +56,6 @@ var BaseAdapter = class {
|
|
|
58
56
|
this.resolvedAuth = resolvedAuth;
|
|
59
57
|
}
|
|
60
58
|
};
|
|
61
|
-
|
|
62
59
|
//#endregion
|
|
63
60
|
//#region src/adapters/ones.ts
|
|
64
61
|
const TASK_DETAIL_QUERY = `
|
|
@@ -92,6 +89,26 @@ const TASK_DETAIL_QUERY = `
|
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
91
|
`;
|
|
92
|
+
const RELATED_ACTIVITIES_QUERY = `
|
|
93
|
+
query Task($key: Key) {
|
|
94
|
+
task(key: $key) {
|
|
95
|
+
key
|
|
96
|
+
...RelatedActivities_task1
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fragment RelatedActivities_task1 on Task {
|
|
101
|
+
relatedActivities {
|
|
102
|
+
uuid
|
|
103
|
+
name
|
|
104
|
+
projectUUID
|
|
105
|
+
project_uuid: projectUUID
|
|
106
|
+
relatedChild
|
|
107
|
+
related_child_uuid: relatedChild
|
|
108
|
+
}
|
|
109
|
+
relatedActivitiesCount
|
|
110
|
+
}
|
|
111
|
+
`;
|
|
95
112
|
const SEARCH_TASKS_QUERY = `
|
|
96
113
|
query GROUP_TASK_DATA($groupBy: GroupBy, $groupOrderBy: OrderBy, $orderBy: OrderBy, $filterGroup: [Filter!], $search: Search, $pagination: Pagination, $limit: Int) {
|
|
97
114
|
buckets(groupBy: $groupBy, orderBy: $groupOrderBy, pagination: $pagination, filter: $search) {
|
|
@@ -116,6 +133,26 @@ const ISSUE_TYPES_QUERY = `
|
|
|
116
133
|
}
|
|
117
134
|
}
|
|
118
135
|
`;
|
|
136
|
+
const PROJECTS_QUERY = `
|
|
137
|
+
query Projects($groupBy: GroupBy, $orderBy: OrderBy, $pagination: Pagination, $projectOrderBy: OrderBy, $projectFilterGroup: [Filter!]) {
|
|
138
|
+
buckets(groupBy: $groupBy, orderBy: $orderBy, pagination: $pagination) {
|
|
139
|
+
key
|
|
140
|
+
projects(limit: 10000, orderBy: $projectOrderBy, filterGroup: $projectFilterGroup) {
|
|
141
|
+
key
|
|
142
|
+
uuid
|
|
143
|
+
name
|
|
144
|
+
identifier
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
`;
|
|
149
|
+
const ADD_MANHOUR_MUTATION = `
|
|
150
|
+
mutation AddManhour {
|
|
151
|
+
addManhour(mode: $mode, owner: $owner, task: $task, type: $type, start_time: $start_time, hours: $hours, description: $description, customData: $customData) {
|
|
152
|
+
key
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
119
156
|
const TASK_BY_NUMBER_QUERY = SEARCH_TASKS_QUERY;
|
|
120
157
|
const RELATED_TASKS_QUERY = `
|
|
121
158
|
query Task($key: Key) {
|
|
@@ -304,7 +341,7 @@ function extractWikiPageUuidsFromText(text) {
|
|
|
304
341
|
return [...uuids];
|
|
305
342
|
}
|
|
306
343
|
function parseOnesWikiPageRoute(input) {
|
|
307
|
-
if (!input
|
|
344
|
+
if (!isOnesWikiUrlInput(input)) return null;
|
|
308
345
|
const match = (() => {
|
|
309
346
|
try {
|
|
310
347
|
const parsed = new URL(input);
|
|
@@ -312,7 +349,7 @@ function parseOnesWikiPageRoute(input) {
|
|
|
312
349
|
} catch {
|
|
313
350
|
return input;
|
|
314
351
|
}
|
|
315
|
-
})().match(/\/team\/([^/?#]+)\/space\/[^/?#]+\/page\/([^/?#]+)/);
|
|
352
|
+
})().match(/\/team\/([^/?#]+)\/(?:space\/[^/?#]+\/)?page\/([^/?#]+)/);
|
|
316
353
|
if (!match?.[1] || !match[2]) return null;
|
|
317
354
|
return {
|
|
318
355
|
teamUuid: decodeURIComponent(match[1]),
|
|
@@ -320,7 +357,75 @@ function parseOnesWikiPageRoute(input) {
|
|
|
320
357
|
};
|
|
321
358
|
}
|
|
322
359
|
function isOnesWikiUrlInput(input) {
|
|
323
|
-
return
|
|
360
|
+
return /\/wiki(?:\/|(?=[#?]|$))/.test(input);
|
|
361
|
+
}
|
|
362
|
+
function parseAuthorizeRequestId(location) {
|
|
363
|
+
try {
|
|
364
|
+
const parsed = new URL(location);
|
|
365
|
+
return parsed.searchParams.get("auth_request_id") ?? parsed.searchParams.get("id");
|
|
366
|
+
} catch {
|
|
367
|
+
const match = location.match(/[?&](?:auth_request_id|id)=([^&#]+)/);
|
|
368
|
+
return match?.[1] ? decodeURIComponent(match[1]) : null;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function parseAuthorizationCode(location) {
|
|
372
|
+
try {
|
|
373
|
+
return new URL(location).searchParams.get("code");
|
|
374
|
+
} catch {
|
|
375
|
+
const match = location.match(/[?&]code=([^&#]+)/);
|
|
376
|
+
return match?.[1] ? decodeURIComponent(match[1]) : null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function parseDisplayId(input) {
|
|
380
|
+
const match = input.trim().match(/^([a-z]\w*)-(\d+)$/i);
|
|
381
|
+
if (!match?.[1] || !match[2]) return null;
|
|
382
|
+
return {
|
|
383
|
+
identifier: match[1],
|
|
384
|
+
number: Number.parseInt(match[2], 10)
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function isValidOnesDate(value) {
|
|
388
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
|
|
389
|
+
const [yearText, monthText, dayText] = value.split("-");
|
|
390
|
+
const year = Number.parseInt(yearText, 10);
|
|
391
|
+
const month = Number.parseInt(monthText, 10);
|
|
392
|
+
const day = Number.parseInt(dayText, 10);
|
|
393
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
394
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
395
|
+
}
|
|
396
|
+
function toOnesHours(hours) {
|
|
397
|
+
if (!Number.isFinite(hours) || hours <= 0) throw new Error("ONES: hours must be a positive number");
|
|
398
|
+
return Math.round(hours * 1e5);
|
|
399
|
+
}
|
|
400
|
+
function getTodayStartUnixSeconds() {
|
|
401
|
+
const now = /* @__PURE__ */ new Date();
|
|
402
|
+
const localStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
403
|
+
return Math.floor(localStart.getTime() / 1e3);
|
|
404
|
+
}
|
|
405
|
+
function toLocalDateString(date) {
|
|
406
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
407
|
+
}
|
|
408
|
+
function getLocalStartUnixSeconds(year, month, day) {
|
|
409
|
+
return Math.floor(new Date(year, month - 1, day).getTime() / 1e3);
|
|
410
|
+
}
|
|
411
|
+
function parseManhourDate(input) {
|
|
412
|
+
const value = input?.trim();
|
|
413
|
+
if (!value) return {
|
|
414
|
+
date: null,
|
|
415
|
+
startTime: getTodayStartUnixSeconds()
|
|
416
|
+
};
|
|
417
|
+
const fullDateMatch = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
418
|
+
const dayOnlyMatch = value.match(/^(\d{1,2})号?$/);
|
|
419
|
+
const now = /* @__PURE__ */ new Date();
|
|
420
|
+
const year = fullDateMatch ? Number.parseInt(fullDateMatch[1], 10) : now.getFullYear();
|
|
421
|
+
const month = fullDateMatch ? Number.parseInt(fullDateMatch[2], 10) : now.getMonth() + 1;
|
|
422
|
+
const day = fullDateMatch ? Number.parseInt(fullDateMatch[3], 10) : dayOnlyMatch ? Number.parseInt(dayOnlyMatch[1], 10) : NaN;
|
|
423
|
+
const parsed = new Date(year, month - 1, day);
|
|
424
|
+
if (!(Number.isInteger(day) && parsed.getFullYear() === year && parsed.getMonth() === month - 1 && parsed.getDate() === day)) throw new Error("ONES: date must be a valid YYYY-MM-DD date or day of current month");
|
|
425
|
+
return {
|
|
426
|
+
date: toLocalDateString(parsed),
|
|
427
|
+
startTime: getLocalStartUnixSeconds(year, month, day)
|
|
428
|
+
};
|
|
324
429
|
}
|
|
325
430
|
function htmlToPlainText(html) {
|
|
326
431
|
return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<[^>]+>/g, "").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\n{3,}/g, "\n\n").trim();
|
|
@@ -376,22 +481,126 @@ function renderWikiEmbed(block, context) {
|
|
|
376
481
|
function escapeWikiTableCell(value) {
|
|
377
482
|
return value.replace(/\|/g, "\\|").replace(/[ \t]*\n+[ \t]*/g, " ").trim();
|
|
378
483
|
}
|
|
484
|
+
function escapeWikiHtml(value) {
|
|
485
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
486
|
+
}
|
|
487
|
+
function parseWikiTableSpan(value) {
|
|
488
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return 1;
|
|
489
|
+
return Math.max(Math.trunc(value), 1);
|
|
490
|
+
}
|
|
491
|
+
function buildWikiTableLayout(block) {
|
|
492
|
+
const columnCount = typeof block.cols === "number" && block.cols > 0 ? Math.trunc(block.cols) : 0;
|
|
493
|
+
const children = Array.isArray(block.children) ? block.children.filter((child) => typeof child === "string") : [];
|
|
494
|
+
if (!columnCount || !children.length) return null;
|
|
495
|
+
const hasDeclaredRows = typeof block.rows === "number" && block.rows > 0;
|
|
496
|
+
const initialRowCount = hasDeclaredRows ? Math.trunc(block.rows) : Math.max(Math.ceil(children.length / columnCount), 1);
|
|
497
|
+
const occupied = [];
|
|
498
|
+
const rows = [];
|
|
499
|
+
const ensureRowCount = (count) => {
|
|
500
|
+
while (occupied.length < count) {
|
|
501
|
+
occupied.push(Array.from({ length: columnCount }).fill(false));
|
|
502
|
+
rows.push([]);
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
ensureRowCount(initialRowCount);
|
|
506
|
+
let cursor = 0;
|
|
507
|
+
let hasMergedCells = false;
|
|
508
|
+
for (const childId of children) {
|
|
509
|
+
while (true) {
|
|
510
|
+
const row = Math.floor(cursor / columnCount);
|
|
511
|
+
const column = cursor % columnCount;
|
|
512
|
+
ensureRowCount(row + 1);
|
|
513
|
+
if (!occupied[row][column]) break;
|
|
514
|
+
cursor += 1;
|
|
515
|
+
}
|
|
516
|
+
const row = Math.floor(cursor / columnCount);
|
|
517
|
+
const column = cursor % columnCount;
|
|
518
|
+
const requestedRowSpan = parseWikiTableSpan(block[`${childId}_rowSpan`]);
|
|
519
|
+
if (!hasDeclaredRows) ensureRowCount(row + requestedRowSpan);
|
|
520
|
+
const rowSpan = Math.min(requestedRowSpan, occupied.length - row);
|
|
521
|
+
const colSpan = Math.min(parseWikiTableSpan(block[`${childId}_colSpan`]), columnCount - column);
|
|
522
|
+
hasMergedCells ||= rowSpan > 1 || colSpan > 1;
|
|
523
|
+
rows[row].push({
|
|
524
|
+
childId,
|
|
525
|
+
row,
|
|
526
|
+
column,
|
|
527
|
+
rowSpan,
|
|
528
|
+
colSpan
|
|
529
|
+
});
|
|
530
|
+
for (let rowOffset = 0; rowOffset < rowSpan; rowOffset += 1) for (let columnOffset = 0; columnOffset < colSpan; columnOffset += 1) occupied[row + rowOffset][column + columnOffset] = true;
|
|
531
|
+
cursor += 1;
|
|
532
|
+
}
|
|
533
|
+
return {
|
|
534
|
+
columnCount,
|
|
535
|
+
rows,
|
|
536
|
+
hasMergedCells
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function wikiCellContainsTable(value) {
|
|
540
|
+
return asWikiBlocks(value).some((block) => block.type === "table");
|
|
541
|
+
}
|
|
542
|
+
function renderWikiTextRunsHtml(value) {
|
|
543
|
+
if (!Array.isArray(value)) return "";
|
|
544
|
+
return value.map((run) => {
|
|
545
|
+
if (!isRecord(run)) return "";
|
|
546
|
+
const attributes = isRecord(run.attributes) ? run.attributes : {};
|
|
547
|
+
let content = escapeWikiHtml(typeof run.insert === "string" ? run.insert.replace(/\u00A0/g, " ") : "").replace(/\n/g, "<br>");
|
|
548
|
+
if (attributes.code) content = `<code>${content}</code>`;
|
|
549
|
+
if (attributes.bold) content = `<strong>${content}</strong>`;
|
|
550
|
+
if (attributes.italic) content = `<em>${content}</em>`;
|
|
551
|
+
if (attributes.underline) content = `<u>${content}</u>`;
|
|
552
|
+
if (attributes.strike) content = `<s>${content}</s>`;
|
|
553
|
+
const link = typeof attributes.link === "string" ? attributes.link : "";
|
|
554
|
+
return link ? `<a href="${escapeWikiHtml(link)}">${content}</a>` : content;
|
|
555
|
+
}).join("");
|
|
556
|
+
}
|
|
557
|
+
function renderWikiCellHtml(value, document, context) {
|
|
558
|
+
return asWikiBlocks(value).map((block) => renderWikiBlockHtml(block, document, context)).filter(Boolean).join("");
|
|
559
|
+
}
|
|
560
|
+
function renderWikiBlockHtml(block, document, context) {
|
|
561
|
+
if (block.type === "table") {
|
|
562
|
+
const layout = buildWikiTableLayout(block);
|
|
563
|
+
return layout ? renderWikiTableHtml(layout, document, context) : "";
|
|
564
|
+
}
|
|
565
|
+
if (block.type === "embed") return `<p>${escapeWikiHtml(renderWikiEmbed(block, context))}</p>`;
|
|
566
|
+
const text = renderWikiTextRunsHtml(block.text);
|
|
567
|
+
if (!text) return "";
|
|
568
|
+
if (block.type === "list") {
|
|
569
|
+
const tag = block.ordered ? "ol" : "ul";
|
|
570
|
+
return `<${tag}><li>${text}</li></${tag}>`;
|
|
571
|
+
}
|
|
572
|
+
if (block.heading) {
|
|
573
|
+
const level = Math.min(Math.max(Math.trunc(block.heading), 1), 6);
|
|
574
|
+
return `<h${level}>${text}</h${level}>`;
|
|
575
|
+
}
|
|
576
|
+
return `<p>${text}</p>`;
|
|
577
|
+
}
|
|
578
|
+
function renderWikiTableHtml(layout, document, context) {
|
|
579
|
+
return `<table>\n<tbody>\n${layout.rows.map((row) => {
|
|
580
|
+
return `<tr>\n${row.map((cell) => {
|
|
581
|
+
const attributes = [cell.rowSpan > 1 ? `rowspan="${cell.rowSpan}"` : "", cell.colSpan > 1 ? `colspan="${cell.colSpan}"` : ""].filter(Boolean);
|
|
582
|
+
const content = renderWikiCellHtml(document[cell.childId], document, context);
|
|
583
|
+
return `<td${attributes.length ? ` ${attributes.join(" ")}` : ""}>${content}</td>`;
|
|
584
|
+
}).join("\n")}\n</tr>`;
|
|
585
|
+
}).join("\n")}\n</tbody>\n</table>`;
|
|
586
|
+
}
|
|
379
587
|
function renderWikiCell(value, document, context) {
|
|
380
588
|
const blocks = asWikiBlocks(value);
|
|
381
589
|
if (!blocks.length) return "";
|
|
382
590
|
return blocks.map((block) => renderWikiBlock(block, document, context)).filter(Boolean).join(" ").replace(/[ \t]*\n+[ \t]*/g, " ").trim();
|
|
383
591
|
}
|
|
384
592
|
function renderWikiTable(block, document, context) {
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
593
|
+
const layout = buildWikiTableLayout(block);
|
|
594
|
+
if (!layout) return "";
|
|
595
|
+
const hasNestedTable = layout.rows.some((row) => row.some((cell) => wikiCellContainsTable(document[cell.childId])));
|
|
596
|
+
if (layout.hasMergedCells || hasNestedTable) return renderWikiTableHtml(layout, document, context);
|
|
388
597
|
const rows = [];
|
|
389
|
-
for (
|
|
390
|
-
const cells =
|
|
391
|
-
|
|
598
|
+
for (const row of layout.rows) {
|
|
599
|
+
const cells = Array.from({ length: layout.columnCount }).fill("");
|
|
600
|
+
for (const cell of row) cells[cell.column] = escapeWikiTableCell(renderWikiCell(document[cell.childId], document, context));
|
|
392
601
|
rows.push(`| ${cells.join(" | ")} |`);
|
|
393
602
|
}
|
|
394
|
-
if (rows.length > 1) rows.splice(1, 0, `| ${Array.from({ length:
|
|
603
|
+
if (rows.length > 1) rows.splice(1, 0, `| ${Array.from({ length: layout.columnCount }).fill("---").join(" | ")} |`);
|
|
395
604
|
return rows.join("\n");
|
|
396
605
|
}
|
|
397
606
|
function renderWikiBlock(block, document, context) {
|
|
@@ -516,32 +725,35 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
516
725
|
redirect: "manual"
|
|
517
726
|
})).headers.get("location");
|
|
518
727
|
if (!authorizeLocation) throw new Error("ONES: Authorize response missing location header");
|
|
519
|
-
|
|
520
|
-
if (!
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
728
|
+
let code = parseAuthorizationCode(authorizeLocation);
|
|
729
|
+
if (!code) {
|
|
730
|
+
const authRequestId = parseAuthorizeRequestId(authorizeLocation);
|
|
731
|
+
if (!authRequestId) throw new Error("ONES: Cannot parse auth_request_id from authorize redirect");
|
|
732
|
+
const finalizeRes = await fetch(`${baseUrl}/identity/api/auth_request/finalize`, {
|
|
733
|
+
method: "POST",
|
|
734
|
+
headers: {
|
|
735
|
+
"Content-Type": "application/json;charset=UTF-8",
|
|
736
|
+
"Cookie": cookies
|
|
737
|
+
},
|
|
738
|
+
body: JSON.stringify({
|
|
739
|
+
auth_request_id: authRequestId,
|
|
740
|
+
region_uuid: orgUser.region_uuid,
|
|
741
|
+
org_uuid: orgUser.org_uuid,
|
|
742
|
+
org_user_uuid: orgUser.org_user.org_user_uuid
|
|
743
|
+
})
|
|
744
|
+
});
|
|
745
|
+
if (!finalizeRes.ok) {
|
|
746
|
+
const text = await finalizeRes.text().catch(() => "");
|
|
747
|
+
throw new Error(`ONES: Finalize failed: ${finalizeRes.status} ${text}`);
|
|
748
|
+
}
|
|
749
|
+
const callbackLocation = (await fetch(`${baseUrl}/identity/authorize/callback?id=${authRequestId}&lang=zh`, {
|
|
750
|
+
method: "GET",
|
|
751
|
+
headers: { Cookie: cookies },
|
|
752
|
+
redirect: "manual"
|
|
753
|
+
})).headers.get("location");
|
|
754
|
+
if (!callbackLocation) throw new Error("ONES: Callback response missing location header");
|
|
755
|
+
code = parseAuthorizationCode(callbackLocation);
|
|
537
756
|
}
|
|
538
|
-
const callbackLocation = (await fetch(`${baseUrl}/identity/authorize/callback?id=${authRequestId}&lang=zh`, {
|
|
539
|
-
method: "GET",
|
|
540
|
-
headers: { Cookie: cookies },
|
|
541
|
-
redirect: "manual"
|
|
542
|
-
})).headers.get("location");
|
|
543
|
-
if (!callbackLocation) throw new Error("ONES: Callback response missing location header");
|
|
544
|
-
const code = new URL(callbackLocation).searchParams.get("code");
|
|
545
757
|
if (!code) throw new Error("ONES: Cannot parse authorization code from callback redirect");
|
|
546
758
|
const tokenRes = await fetch(`${baseUrl}/identity/oauth/token`, {
|
|
547
759
|
method: "POST",
|
|
@@ -611,11 +823,136 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
611
823
|
}
|
|
612
824
|
return response.json();
|
|
613
825
|
}
|
|
826
|
+
async onesql(query, variables, workItemType) {
|
|
827
|
+
const session = await this.login();
|
|
828
|
+
const url = `${this.config.apiBase}/project/api/ones-project/team/${session.teamUuid}/workitems/onesql`;
|
|
829
|
+
const response = await fetch(url, {
|
|
830
|
+
method: "POST",
|
|
831
|
+
headers: {
|
|
832
|
+
"Authorization": `Bearer ${session.accessToken}`,
|
|
833
|
+
"Content-Type": "application/json"
|
|
834
|
+
},
|
|
835
|
+
body: JSON.stringify({
|
|
836
|
+
query,
|
|
837
|
+
variables: [
|
|
838
|
+
variables,
|
|
839
|
+
workItemType,
|
|
840
|
+
null,
|
|
841
|
+
null
|
|
842
|
+
]
|
|
843
|
+
})
|
|
844
|
+
});
|
|
845
|
+
if (!response.ok) {
|
|
846
|
+
const text = await response.text().catch(() => "");
|
|
847
|
+
throw new Error(`ONES OneSQL error: ${response.status} ${text}`);
|
|
848
|
+
}
|
|
849
|
+
return response.json();
|
|
850
|
+
}
|
|
851
|
+
async fetchRelatedActivities(taskKey) {
|
|
852
|
+
return (await this.onesql(RELATED_ACTIVITIES_QUERY, { key: taskKey }, "Task")).data?.task?.relatedActivities ?? [];
|
|
853
|
+
}
|
|
854
|
+
async searchTaskByNumber(taskNumber) {
|
|
855
|
+
const session = await this.login();
|
|
856
|
+
const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/search?q=${encodeURIComponent(String(taskNumber))}&start=0&limit=10&types=task`;
|
|
857
|
+
const response = await fetch(url, { headers: { Authorization: `Bearer ${session.accessToken}` } });
|
|
858
|
+
if (!response.ok) return null;
|
|
859
|
+
const found = ((await response.json()).datas?.task ?? []).map((item) => item.fields).find((fields) => fields?.uuid && fields.number === taskNumber);
|
|
860
|
+
if (!found?.uuid) return null;
|
|
861
|
+
return {
|
|
862
|
+
key: `task-${found.uuid}`,
|
|
863
|
+
uuid: found.uuid,
|
|
864
|
+
number: found.number ?? taskNumber,
|
|
865
|
+
name: found.summary ?? "",
|
|
866
|
+
status: {
|
|
867
|
+
uuid: "",
|
|
868
|
+
name: "",
|
|
869
|
+
category: void 0
|
|
870
|
+
},
|
|
871
|
+
issueType: found.issue_type_uuid || found.issue_type_name ? {
|
|
872
|
+
uuid: found.issue_type_uuid ?? "",
|
|
873
|
+
name: found.issue_type_name ?? ""
|
|
874
|
+
} : void 0,
|
|
875
|
+
project: found.project_uuid || found.project_name ? {
|
|
876
|
+
uuid: found.project_uuid ?? "",
|
|
877
|
+
name: found.project_name ?? ""
|
|
878
|
+
} : void 0
|
|
879
|
+
};
|
|
880
|
+
}
|
|
614
881
|
async fetchIssueTypes() {
|
|
615
882
|
if (this.issueTypesCache) return this.issueTypesCache;
|
|
616
|
-
|
|
883
|
+
const data = await this.graphql(ISSUE_TYPES_QUERY, { orderBy: { namePinyin: "ASC" } }, "issueTypes");
|
|
884
|
+
this.issueTypesCache = data.data?.issueTypes ?? [];
|
|
617
885
|
return this.issueTypesCache;
|
|
618
886
|
}
|
|
887
|
+
async fetchProjects() {
|
|
888
|
+
return (await this.graphql(PROJECTS_QUERY, {
|
|
889
|
+
projectOrderBy: {
|
|
890
|
+
isPin: "DESC",
|
|
891
|
+
namePinyin: "ASC",
|
|
892
|
+
createTime: "DESC"
|
|
893
|
+
},
|
|
894
|
+
projectFilterGroup: [{
|
|
895
|
+
visibleInProject_equal: true,
|
|
896
|
+
isArchive_equal: false
|
|
897
|
+
}],
|
|
898
|
+
groupBy: { projects: {} },
|
|
899
|
+
orderBy: null,
|
|
900
|
+
pagination: {
|
|
901
|
+
limit: 50,
|
|
902
|
+
after: "",
|
|
903
|
+
preciseCount: true
|
|
904
|
+
}
|
|
905
|
+
}, "projects-group-list-for-project-view")).data?.buckets?.flatMap((bucket) => bucket.projects ?? []) ?? [];
|
|
906
|
+
}
|
|
907
|
+
async findTaskByNumber(taskNumber, projectUuid) {
|
|
908
|
+
const filter = { number_in: [taskNumber] };
|
|
909
|
+
if (projectUuid) filter.project_in = [projectUuid];
|
|
910
|
+
const found = ((await this.graphql(TASK_BY_NUMBER_QUERY, {
|
|
911
|
+
groupBy: { tasks: {} },
|
|
912
|
+
groupOrderBy: null,
|
|
913
|
+
orderBy: { createTime: "DESC" },
|
|
914
|
+
filterGroup: [filter],
|
|
915
|
+
search: null,
|
|
916
|
+
pagination: {
|
|
917
|
+
limit: 10,
|
|
918
|
+
preciseCount: false
|
|
919
|
+
},
|
|
920
|
+
limit: 10
|
|
921
|
+
}, "group-task-data")).data?.buckets?.flatMap((b) => b.tasks ?? []) ?? []).find((task) => task.number === taskNumber && (!projectUuid || task.project?.uuid === projectUuid));
|
|
922
|
+
if (found) return found;
|
|
923
|
+
if (projectUuid) return null;
|
|
924
|
+
return this.searchTaskByNumber(taskNumber);
|
|
925
|
+
}
|
|
926
|
+
async resolveTaskRef(input) {
|
|
927
|
+
const taskId = input.trim();
|
|
928
|
+
if (!taskId) throw new Error("ONES: taskId is required");
|
|
929
|
+
const numMatch = taskId.match(/^#?(\d+)$/);
|
|
930
|
+
if (numMatch) {
|
|
931
|
+
const taskNumber = Number.parseInt(numMatch[1], 10);
|
|
932
|
+
const found = await this.findTaskByNumber(taskNumber);
|
|
933
|
+
if (!found) throw new Error(`ONES: Task #${taskNumber} not found in current team`);
|
|
934
|
+
return {
|
|
935
|
+
key: found.key ?? `task-${found.uuid}`,
|
|
936
|
+
uuid: found.uuid
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
const displayId = parseDisplayId(taskId);
|
|
940
|
+
if (displayId) {
|
|
941
|
+
const project = (await this.fetchProjects()).find((item) => item.identifier?.toLowerCase() === displayId.identifier.toLowerCase());
|
|
942
|
+
if (!project) throw new Error(`ONES: Project identifier "${displayId.identifier}" not found in current team`);
|
|
943
|
+
const found = await this.findTaskByNumber(displayId.number, project.uuid);
|
|
944
|
+
if (!found) throw new Error(`ONES: Task "${taskId}" not found in current team`);
|
|
945
|
+
return {
|
|
946
|
+
key: found.key ?? `task-${found.uuid}`,
|
|
947
|
+
uuid: found.uuid
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
const key = taskId.startsWith("task-") ? taskId : `task-${taskId}`;
|
|
951
|
+
return {
|
|
952
|
+
key,
|
|
953
|
+
uuid: key.slice(5)
|
|
954
|
+
};
|
|
955
|
+
}
|
|
619
956
|
async searchTeamUsers(keyword) {
|
|
620
957
|
const session = await this.login();
|
|
621
958
|
const url = `${this.config.apiBase}/project/api/project/team/${session.teamUuid}/users/search`;
|
|
@@ -804,27 +1141,11 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
804
1141
|
};
|
|
805
1142
|
}
|
|
806
1143
|
if (isOnesWikiUrlInput(params.id)) throw new Error("ONES: Unsupported wiki page URL. Expected /wiki/#/team/{teamUuid}/space/{spaceUuid}/page/{wikiUuid}");
|
|
807
|
-
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
groupBy: { tasks: {} },
|
|
813
|
-
groupOrderBy: null,
|
|
814
|
-
orderBy: { createTime: "DESC" },
|
|
815
|
-
filterGroup: [{ number_in: [taskNumber] }],
|
|
816
|
-
search: null,
|
|
817
|
-
pagination: {
|
|
818
|
-
limit: 10,
|
|
819
|
-
preciseCount: false
|
|
820
|
-
},
|
|
821
|
-
limit: 10
|
|
822
|
-
}, "group-task-data")).data?.buckets?.flatMap((b) => b.tasks ?? []) ?? []).find((t) => t.number === taskNumber);
|
|
823
|
-
if (!found) throw new Error(`ONES: Task #${taskNumber} not found in current team`);
|
|
824
|
-
taskUuid = found.uuid;
|
|
825
|
-
}
|
|
826
|
-
const task = (await this.graphql(TASK_DETAIL_QUERY, { key: `task-${taskUuid}` }, "Task")).data?.task;
|
|
827
|
-
if (!task) throw new Error(`ONES: Task "${taskUuid}" not found`);
|
|
1144
|
+
const taskRef = await this.resolveTaskRef(params.id);
|
|
1145
|
+
const shouldFetchRelatedActivities = parseDisplayId(params.id.trim()) !== null;
|
|
1146
|
+
const task = (await this.graphql(TASK_DETAIL_QUERY, { key: taskRef.key }, "Task")).data?.task;
|
|
1147
|
+
if (!task) throw new Error(`ONES: Task "${params.id}" not found`);
|
|
1148
|
+
const relatedActivities = shouldFetchRelatedActivities ? await this.fetchRelatedActivities(taskRef.key) : [];
|
|
828
1149
|
const wikiRefs = /* @__PURE__ */ new Map();
|
|
829
1150
|
for (const wiki of task.relatedWikiPages ?? []) if (!wiki.errorMessage) wikiRefs.set(wiki.uuid, {
|
|
830
1151
|
title: wiki.title,
|
|
@@ -865,6 +1186,18 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
865
1186
|
parts.push(`- #${related.number} ${related.name} [${related.issueType?.name}] (${related.status?.name}) — ${assignee}`);
|
|
866
1187
|
}
|
|
867
1188
|
}
|
|
1189
|
+
if (relatedActivities.length) {
|
|
1190
|
+
parts.push("");
|
|
1191
|
+
parts.push("## Related Work Items");
|
|
1192
|
+
for (const activity of relatedActivities) {
|
|
1193
|
+
const details = [
|
|
1194
|
+
`UUID: ${activity.uuid}`,
|
|
1195
|
+
activity.projectUUID ? `Project: ${activity.projectUUID}` : null,
|
|
1196
|
+
activity.relatedChild ? `Relation: ${activity.relatedChild}` : null
|
|
1197
|
+
].filter(Boolean);
|
|
1198
|
+
parts.push(`- ${activity.name} (${details.join(", ")})`);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
868
1201
|
if (task.parent?.uuid) {
|
|
869
1202
|
parts.push("");
|
|
870
1203
|
parts.push("## Parent Task");
|
|
@@ -895,7 +1228,12 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
895
1228
|
parts.push(detailText);
|
|
896
1229
|
}
|
|
897
1230
|
const wikiAttachments = wikiContents.flatMap((wiki) => wiki.attachments);
|
|
898
|
-
|
|
1231
|
+
const req = toRequirement(task, parts.join("\n"), wikiAttachments);
|
|
1232
|
+
req.raw = {
|
|
1233
|
+
...req.raw,
|
|
1234
|
+
relatedActivities
|
|
1235
|
+
};
|
|
1236
|
+
return req;
|
|
899
1237
|
}
|
|
900
1238
|
/**
|
|
901
1239
|
* Search tasks assigned to current user via GraphQL.
|
|
@@ -959,6 +1297,69 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
959
1297
|
pageSize
|
|
960
1298
|
};
|
|
961
1299
|
}
|
|
1300
|
+
async addManhour(params) {
|
|
1301
|
+
const description = params.description.trim();
|
|
1302
|
+
if (!description) throw new Error("ONES: description is required");
|
|
1303
|
+
const taskRef = await this.resolveTaskRef(params.taskId);
|
|
1304
|
+
const onesHours = toOnesHours(params.hours);
|
|
1305
|
+
const workDate = parseManhourDate(params.date);
|
|
1306
|
+
const key = (await this.graphql(ADD_MANHOUR_MUTATION, {
|
|
1307
|
+
mode: "simple",
|
|
1308
|
+
type: "recorded",
|
|
1309
|
+
customData: {},
|
|
1310
|
+
owner: (await this.login()).userUuid,
|
|
1311
|
+
task: taskRef.uuid,
|
|
1312
|
+
start_time: workDate.startTime,
|
|
1313
|
+
hours: onesHours,
|
|
1314
|
+
description
|
|
1315
|
+
}, "add-manhour")).data?.addManhour?.key;
|
|
1316
|
+
if (!key) throw new Error("ONES: Failed to add manhour");
|
|
1317
|
+
return {
|
|
1318
|
+
key,
|
|
1319
|
+
taskUuid: taskRef.uuid,
|
|
1320
|
+
hours: params.hours,
|
|
1321
|
+
description,
|
|
1322
|
+
date: workDate.date
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
async updateTaskPlanDates(params) {
|
|
1326
|
+
const planStartDate = params.planStartDate?.trim();
|
|
1327
|
+
const planEndDate = params.planEndDate?.trim();
|
|
1328
|
+
if (!planStartDate && !planEndDate) throw new Error("ONES: planStartDate or planEndDate is required");
|
|
1329
|
+
if (planStartDate && !isValidOnesDate(planStartDate)) throw new Error("ONES: planStartDate must be a valid YYYY-MM-DD date");
|
|
1330
|
+
if (planEndDate && !isValidOnesDate(planEndDate)) throw new Error("ONES: planEndDate must be a valid YYYY-MM-DD date");
|
|
1331
|
+
const taskRef = await this.resolveTaskRef(params.taskId);
|
|
1332
|
+
const session = await this.login();
|
|
1333
|
+
const fieldValues = [];
|
|
1334
|
+
if (planStartDate) fieldValues.push({
|
|
1335
|
+
field_uuid: "field027",
|
|
1336
|
+
value: planStartDate
|
|
1337
|
+
});
|
|
1338
|
+
if (planEndDate) fieldValues.push({
|
|
1339
|
+
field_uuid: "field028",
|
|
1340
|
+
value: planEndDate
|
|
1341
|
+
});
|
|
1342
|
+
const response = await fetch(`${this.config.apiBase}/project/api/project/team/${session.teamUuid}/tasks/update3`, {
|
|
1343
|
+
method: "POST",
|
|
1344
|
+
headers: {
|
|
1345
|
+
"Authorization": `Bearer ${session.accessToken}`,
|
|
1346
|
+
"Content-Type": "application/json"
|
|
1347
|
+
},
|
|
1348
|
+
body: JSON.stringify({ tasks: [{
|
|
1349
|
+
uuid: taskRef.uuid,
|
|
1350
|
+
field_values: fieldValues
|
|
1351
|
+
}] })
|
|
1352
|
+
});
|
|
1353
|
+
if (!response.ok) {
|
|
1354
|
+
const text = await response.text().catch(() => "");
|
|
1355
|
+
throw new Error(`ONES: Failed to update task plan dates: ${response.status} ${text}`);
|
|
1356
|
+
}
|
|
1357
|
+
return {
|
|
1358
|
+
taskUuid: taskRef.uuid,
|
|
1359
|
+
planStartDate: planStartDate ?? null,
|
|
1360
|
+
planEndDate: planEndDate ?? null
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
962
1363
|
async getRelatedIssues(params) {
|
|
963
1364
|
const session = await this.login();
|
|
964
1365
|
const taskKey = params.taskId.startsWith("task-") ? params.taskId : `task-${params.taskId}`;
|
|
@@ -1135,7 +1536,6 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1135
1536
|
};
|
|
1136
1537
|
}
|
|
1137
1538
|
};
|
|
1138
|
-
|
|
1139
1539
|
//#endregion
|
|
1140
1540
|
//#region src/adapters/index.ts
|
|
1141
1541
|
const ADAPTER_MAP = { ones: OnesAdapter };
|
|
@@ -1147,7 +1547,6 @@ function createAdapter(sourceType, config, resolvedAuth) {
|
|
|
1147
1547
|
if (!AdapterClass) throw new Error(`Unsupported source type: "${sourceType}". Supported: ${Object.keys(ADAPTER_MAP).join(", ")}`);
|
|
1148
1548
|
return new AdapterClass(sourceType, config, resolvedAuth);
|
|
1149
1549
|
}
|
|
1150
|
-
|
|
1151
1550
|
//#endregion
|
|
1152
1551
|
//#region src/config/loader.ts
|
|
1153
1552
|
const AuthSchema = z.discriminatedUnion("type", [
|
|
@@ -1305,7 +1704,41 @@ function loadConfig(startDir) {
|
|
|
1305
1704
|
configPath
|
|
1306
1705
|
};
|
|
1307
1706
|
}
|
|
1308
|
-
|
|
1707
|
+
//#endregion
|
|
1708
|
+
//#region src/tools/add-manhour.ts
|
|
1709
|
+
const AddManhourSchema = z.object({
|
|
1710
|
+
taskId: z.string().min(1).describe("The task or requirement ID, key, number, or displayId (e.g. \"task-mock-uuid\", \"mock-uuid\", \"1001\", or \"DEMO-1001\")"),
|
|
1711
|
+
hours: z.number().positive().describe("Work hours to record. Natural hours are converted to ONES internal units."),
|
|
1712
|
+
description: z.string().min(1).describe("Work log description."),
|
|
1713
|
+
date: z.string().optional().describe("Optional work date. Accepts YYYY-MM-DD, a day number like \"11\", or a day-of-month phrase like \"11号\"; day-only values use the current year and month."),
|
|
1714
|
+
source: z.string().optional().describe("Source to update. If omitted, uses the default source.")
|
|
1715
|
+
});
|
|
1716
|
+
async function handleAddManhour(input, adapters, defaultSource) {
|
|
1717
|
+
const sourceType = input.source ?? defaultSource;
|
|
1718
|
+
if (!sourceType) throw new Error("No source specified and no default source configured");
|
|
1719
|
+
const adapter = adapters.get(sourceType);
|
|
1720
|
+
if (!adapter) throw new Error(`Source "${sourceType}" is not configured. Available: ${[...adapters.keys()].join(", ")}`);
|
|
1721
|
+
return { content: [{
|
|
1722
|
+
type: "text",
|
|
1723
|
+
text: formatAddManhourResult(await adapter.addManhour({
|
|
1724
|
+
taskId: input.taskId,
|
|
1725
|
+
hours: input.hours,
|
|
1726
|
+
description: input.description,
|
|
1727
|
+
date: input.date
|
|
1728
|
+
}))
|
|
1729
|
+
}] };
|
|
1730
|
+
}
|
|
1731
|
+
function formatAddManhourResult(result) {
|
|
1732
|
+
return [
|
|
1733
|
+
"Added manhour.",
|
|
1734
|
+
"",
|
|
1735
|
+
`- **Key**: ${result.key}`,
|
|
1736
|
+
`- **Task UUID**: ${result.taskUuid}`,
|
|
1737
|
+
`- **Hours**: ${result.hours}`,
|
|
1738
|
+
`- **Date**: ${result.date ?? "today"}`,
|
|
1739
|
+
`- **Description**: ${result.description}`
|
|
1740
|
+
].join("\n");
|
|
1741
|
+
}
|
|
1309
1742
|
//#endregion
|
|
1310
1743
|
//#region src/tools/get-issue-detail.ts
|
|
1311
1744
|
const GetIssueDetailSchema = z.object({
|
|
@@ -1380,7 +1813,6 @@ function formatIssueDetail(detail) {
|
|
|
1380
1813
|
else lines.push("_No description_");
|
|
1381
1814
|
return lines.join("\n");
|
|
1382
1815
|
}
|
|
1383
|
-
|
|
1384
1816
|
//#endregion
|
|
1385
1817
|
//#region src/tools/get-related-issues.ts
|
|
1386
1818
|
const GetRelatedIssuesSchema = z.object({
|
|
@@ -1421,7 +1853,6 @@ function formatRelatedIssues(issues) {
|
|
|
1421
1853
|
}
|
|
1422
1854
|
return lines.join("\n");
|
|
1423
1855
|
}
|
|
1424
|
-
|
|
1425
1856
|
//#endregion
|
|
1426
1857
|
//#region src/tools/get-requirement.ts
|
|
1427
1858
|
const GetRequirementSchema = z.object({
|
|
@@ -1500,7 +1931,6 @@ function formatRequirement(req) {
|
|
|
1500
1931
|
}
|
|
1501
1932
|
return lines.join("\n");
|
|
1502
1933
|
}
|
|
1503
|
-
|
|
1504
1934
|
//#endregion
|
|
1505
1935
|
//#region src/tools/get-testcases.ts
|
|
1506
1936
|
const GetTestcasesSchema = z.object({
|
|
@@ -1552,7 +1982,6 @@ function formatTestcases(result) {
|
|
|
1552
1982
|
}
|
|
1553
1983
|
return lines.join("\n");
|
|
1554
1984
|
}
|
|
1555
|
-
|
|
1556
1985
|
//#endregion
|
|
1557
1986
|
//#region src/tools/list-sources.ts
|
|
1558
1987
|
async function handleListSources(adapters, config) {
|
|
@@ -1578,7 +2007,6 @@ async function handleListSources(adapters, config) {
|
|
|
1578
2007
|
text: lines.join("\n")
|
|
1579
2008
|
}] };
|
|
1580
2009
|
}
|
|
1581
|
-
|
|
1582
2010
|
//#endregion
|
|
1583
2011
|
//#region src/tools/search-requirements.ts
|
|
1584
2012
|
const SearchRequirementsSchema = z.object({
|
|
@@ -1619,7 +2047,39 @@ async function handleSearchRequirements(input, adapters, defaultSource) {
|
|
|
1619
2047
|
text: lines.join("\n")
|
|
1620
2048
|
}] };
|
|
1621
2049
|
}
|
|
1622
|
-
|
|
2050
|
+
//#endregion
|
|
2051
|
+
//#region src/tools/update-task-plan-dates.ts
|
|
2052
|
+
const DateSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Expected YYYY-MM-DD");
|
|
2053
|
+
const UpdateTaskPlanDatesSchema = z.object({
|
|
2054
|
+
taskId: z.string().min(1).describe("The task or requirement ID, key, number, or displayId (e.g. \"task-mock-uuid\", \"mock-uuid\", \"1001\", or \"DEMO-1001\")"),
|
|
2055
|
+
planStartDate: DateSchema.optional().describe("Plan start date in YYYY-MM-DD format."),
|
|
2056
|
+
planEndDate: DateSchema.optional().describe("Plan end date in YYYY-MM-DD format."),
|
|
2057
|
+
source: z.string().optional().describe("Source to update. If omitted, uses the default source.")
|
|
2058
|
+
});
|
|
2059
|
+
async function handleUpdateTaskPlanDates(input, adapters, defaultSource) {
|
|
2060
|
+
const sourceType = input.source ?? defaultSource;
|
|
2061
|
+
if (!sourceType) throw new Error("No source specified and no default source configured");
|
|
2062
|
+
const adapter = adapters.get(sourceType);
|
|
2063
|
+
if (!adapter) throw new Error(`Source "${sourceType}" is not configured. Available: ${[...adapters.keys()].join(", ")}`);
|
|
2064
|
+
return { content: [{
|
|
2065
|
+
type: "text",
|
|
2066
|
+
text: formatUpdateTaskPlanDatesResult(await adapter.updateTaskPlanDates({
|
|
2067
|
+
taskId: input.taskId,
|
|
2068
|
+
planStartDate: input.planStartDate,
|
|
2069
|
+
planEndDate: input.planEndDate
|
|
2070
|
+
}))
|
|
2071
|
+
}] };
|
|
2072
|
+
}
|
|
2073
|
+
function formatUpdateTaskPlanDatesResult(result) {
|
|
2074
|
+
const lines = [
|
|
2075
|
+
"Updated task plan dates.",
|
|
2076
|
+
"",
|
|
2077
|
+
`- **Task UUID**: ${result.taskUuid}`
|
|
2078
|
+
];
|
|
2079
|
+
if (result.planStartDate) lines.push(`- **Plan Start Date**: ${result.planStartDate}`);
|
|
2080
|
+
if (result.planEndDate) lines.push(`- **Plan End Date**: ${result.planEndDate}`);
|
|
2081
|
+
return lines.join("\n");
|
|
2082
|
+
}
|
|
1623
2083
|
//#endregion
|
|
1624
2084
|
//#region src/index.ts
|
|
1625
2085
|
/**
|
|
@@ -1667,7 +2127,10 @@ async function main() {
|
|
|
1667
2127
|
name: "ai-dev-requirements",
|
|
1668
2128
|
version: "0.1.0"
|
|
1669
2129
|
});
|
|
1670
|
-
server.
|
|
2130
|
+
server.registerTool("get_requirement", {
|
|
2131
|
+
description: "Fetch a single requirement/issue by its ID from a configured source (ONES)",
|
|
2132
|
+
inputSchema: GetRequirementSchema.shape
|
|
2133
|
+
}, async (params) => {
|
|
1671
2134
|
try {
|
|
1672
2135
|
return await handleGetRequirement(params, adapters, config.config.defaultSource);
|
|
1673
2136
|
} catch (err) {
|
|
@@ -1680,7 +2143,10 @@ async function main() {
|
|
|
1680
2143
|
};
|
|
1681
2144
|
}
|
|
1682
2145
|
});
|
|
1683
|
-
server.
|
|
2146
|
+
server.registerTool("search_requirements", {
|
|
2147
|
+
description: "Search for requirements/issues by keywords across a configured source",
|
|
2148
|
+
inputSchema: SearchRequirementsSchema.shape
|
|
2149
|
+
}, async (params) => {
|
|
1684
2150
|
try {
|
|
1685
2151
|
return await handleSearchRequirements(params, adapters, config.config.defaultSource);
|
|
1686
2152
|
} catch (err) {
|
|
@@ -1693,7 +2159,7 @@ async function main() {
|
|
|
1693
2159
|
};
|
|
1694
2160
|
}
|
|
1695
2161
|
});
|
|
1696
|
-
server.
|
|
2162
|
+
server.registerTool("list_sources", { description: "List all configured requirement sources and their status" }, async () => {
|
|
1697
2163
|
try {
|
|
1698
2164
|
return await handleListSources(adapters, config.config);
|
|
1699
2165
|
} catch (err) {
|
|
@@ -1706,7 +2172,10 @@ async function main() {
|
|
|
1706
2172
|
};
|
|
1707
2173
|
}
|
|
1708
2174
|
});
|
|
1709
|
-
server.
|
|
2175
|
+
server.registerTool("get_related_issues", {
|
|
2176
|
+
description: "Get pending defect issues (bugs) related to a requirement task. Returns all pending defects grouped by assignee (current user first).",
|
|
2177
|
+
inputSchema: GetRelatedIssuesSchema.shape
|
|
2178
|
+
}, async (params) => {
|
|
1710
2179
|
try {
|
|
1711
2180
|
return await handleGetRelatedIssues(params, adapters, config.config.defaultSource);
|
|
1712
2181
|
} catch (err) {
|
|
@@ -1719,7 +2188,10 @@ async function main() {
|
|
|
1719
2188
|
};
|
|
1720
2189
|
}
|
|
1721
2190
|
});
|
|
1722
|
-
server.
|
|
2191
|
+
server.registerTool("get_issue_detail", {
|
|
2192
|
+
description: "Get detailed information about a specific issue/defect including description, rich text, and images",
|
|
2193
|
+
inputSchema: GetIssueDetailSchema.shape
|
|
2194
|
+
}, async (params) => {
|
|
1723
2195
|
try {
|
|
1724
2196
|
return await handleGetIssueDetail(params, adapters, config.config.defaultSource);
|
|
1725
2197
|
} catch (err) {
|
|
@@ -1732,7 +2204,10 @@ async function main() {
|
|
|
1732
2204
|
};
|
|
1733
2205
|
}
|
|
1734
2206
|
});
|
|
1735
|
-
server.
|
|
2207
|
+
server.registerTool("get_testcases", {
|
|
2208
|
+
description: "Get all test cases for a task by its number (e.g. 302). Searches the testcase library for a matching module and returns all cases with steps.",
|
|
2209
|
+
inputSchema: GetTestcasesSchema.shape
|
|
2210
|
+
}, async (params) => {
|
|
1736
2211
|
try {
|
|
1737
2212
|
return await handleGetTestcases(params, adapters, config.config.defaultSource);
|
|
1738
2213
|
} catch (err) {
|
|
@@ -1745,6 +2220,38 @@ async function main() {
|
|
|
1745
2220
|
};
|
|
1746
2221
|
}
|
|
1747
2222
|
});
|
|
2223
|
+
server.registerTool("add_manhour", {
|
|
2224
|
+
description: "Add a work-hour record to a ONES task, bug, or requirement. Supports task key, uuid, number, or displayId.",
|
|
2225
|
+
inputSchema: AddManhourSchema.shape
|
|
2226
|
+
}, async (params) => {
|
|
2227
|
+
try {
|
|
2228
|
+
return await handleAddManhour(params, adapters, config.config.defaultSource);
|
|
2229
|
+
} catch (err) {
|
|
2230
|
+
return {
|
|
2231
|
+
content: [{
|
|
2232
|
+
type: "text",
|
|
2233
|
+
text: `Error: ${err.message}`
|
|
2234
|
+
}],
|
|
2235
|
+
isError: true
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
});
|
|
2239
|
+
server.registerTool("update_task_plan_dates", {
|
|
2240
|
+
description: "Update plan start and/or plan end dates for a ONES task, bug, or requirement. Supports task key, uuid, number, or displayId.",
|
|
2241
|
+
inputSchema: UpdateTaskPlanDatesSchema.shape
|
|
2242
|
+
}, async (params) => {
|
|
2243
|
+
try {
|
|
2244
|
+
return await handleUpdateTaskPlanDates(params, adapters, config.config.defaultSource);
|
|
2245
|
+
} catch (err) {
|
|
2246
|
+
return {
|
|
2247
|
+
content: [{
|
|
2248
|
+
type: "text",
|
|
2249
|
+
text: `Error: ${err.message}`
|
|
2250
|
+
}],
|
|
2251
|
+
isError: true
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
});
|
|
1748
2255
|
const transport = new StdioServerTransport();
|
|
1749
2256
|
await server.connect(transport);
|
|
1750
2257
|
}
|
|
@@ -1752,7 +2259,7 @@ main().catch((err) => {
|
|
|
1752
2259
|
console.error("[requirements-mcp] Fatal error:", err);
|
|
1753
2260
|
process.exit(1);
|
|
1754
2261
|
});
|
|
1755
|
-
|
|
1756
2262
|
//#endregion
|
|
1757
|
-
export {
|
|
2263
|
+
export {};
|
|
2264
|
+
|
|
1758
2265
|
//# sourceMappingURL=index.mjs.map
|