@sideboard-ai/core 0.1.184 → 0.1.185
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/{chunk-4IQQEJ2C.js → chunk-G4VAMUCL.js} +3 -3
- package/dist/{chunk-IMCIHSIF.js → chunk-IQC35TVQ.js} +3 -3
- package/dist/index.cjs +91 -59
- package/dist/index.d.cts +8 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +91 -59
- package/dist/mcp/run-stdio.cjs +91 -59
- package/dist/mcp/run-stdio.js +91 -59
- package/dist/{orchestrator-4GLAG7YH.js → orchestrator-45PPV2LQ.js} +1 -1
- package/dist/{orchestrator-DKVX67MH.js → orchestrator-SVFF5CLQ.js} +1 -1
- package/package.json +1 -1
|
@@ -467,7 +467,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
467
467
|
await continueOnReply(threadId, prompt);
|
|
468
468
|
return;
|
|
469
469
|
}
|
|
470
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
470
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-SVFF5CLQ.js");
|
|
471
471
|
await getOrchestrator2().send(threadId, prompt);
|
|
472
472
|
} catch {
|
|
473
473
|
}
|
|
@@ -3193,7 +3193,7 @@ function classifyWorktreeOwnership(group, viewerLogin = "") {
|
|
|
3193
3193
|
const reviewing = group.some((t) => {
|
|
3194
3194
|
const author = normalizeViewerLogin(t.prAuthorLogin);
|
|
3195
3195
|
if (author && me && author !== me) return true;
|
|
3196
|
-
if (t.sourceType === "pr" && (!author ||
|
|
3196
|
+
if (t.sourceType === "pr" && me && (!author || author !== me)) return true;
|
|
3197
3197
|
if (me && loginsInclude(t.prReviewerLogins, me)) return true;
|
|
3198
3198
|
return false;
|
|
3199
3199
|
});
|
|
@@ -6766,7 +6766,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
6766
6766
|
${prompt}`;
|
|
6767
6767
|
}
|
|
6768
6768
|
async function defaultDeps() {
|
|
6769
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
6769
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-SVFF5CLQ.js");
|
|
6770
6770
|
const orch = getOrchestrator2();
|
|
6771
6771
|
return {
|
|
6772
6772
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -402,7 +402,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
402
402
|
await continueOnReply(threadId, prompt);
|
|
403
403
|
return;
|
|
404
404
|
}
|
|
405
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
405
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-45PPV2LQ.js");
|
|
406
406
|
await getOrchestrator2().send(threadId, prompt);
|
|
407
407
|
} catch {
|
|
408
408
|
}
|
|
@@ -3101,7 +3101,7 @@ function classifyWorktreeOwnership(group, viewerLogin = "") {
|
|
|
3101
3101
|
const reviewing = group.some((t) => {
|
|
3102
3102
|
const author = normalizeViewerLogin(t.prAuthorLogin);
|
|
3103
3103
|
if (author && me && author !== me) return true;
|
|
3104
|
-
if (t.sourceType === "pr" && (!author ||
|
|
3104
|
+
if (t.sourceType === "pr" && me && (!author || author !== me)) return true;
|
|
3105
3105
|
if (me && loginsInclude(t.prReviewerLogins, me)) return true;
|
|
3106
3106
|
return false;
|
|
3107
3107
|
});
|
|
@@ -6419,7 +6419,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
6419
6419
|
${prompt}`;
|
|
6420
6420
|
}
|
|
6421
6421
|
async function defaultDeps() {
|
|
6422
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
6422
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-45PPV2LQ.js");
|
|
6423
6423
|
const orch = getOrchestrator2();
|
|
6424
6424
|
return {
|
|
6425
6425
|
findThread: (id) => findThreadByRef(id),
|
package/dist/index.cjs
CHANGED
|
@@ -16349,7 +16349,7 @@ function classifyWorktreeOwnership(group, viewerLogin = "") {
|
|
|
16349
16349
|
const reviewing = group.some((t) => {
|
|
16350
16350
|
const author = normalizeViewerLogin(t.prAuthorLogin);
|
|
16351
16351
|
if (author && me && author !== me) return true;
|
|
16352
|
-
if (t.sourceType === "pr" && (!author ||
|
|
16352
|
+
if (t.sourceType === "pr" && me && (!author || author !== me)) return true;
|
|
16353
16353
|
if (me && loginsInclude(t.prReviewerLogins, me)) return true;
|
|
16354
16354
|
return false;
|
|
16355
16355
|
});
|
|
@@ -24622,12 +24622,27 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
24622
24622
|
function assigneeFilterKey(assignee) {
|
|
24623
24623
|
return (assignee ?? "").trim();
|
|
24624
24624
|
}
|
|
24625
|
+
function applyLinearIssueQueryFilter(filter, query) {
|
|
24626
|
+
const term = query?.trim();
|
|
24627
|
+
if (!term) return;
|
|
24628
|
+
const or = [
|
|
24629
|
+
{ title: { containsIgnoreCase: term } },
|
|
24630
|
+
{ description: { containsIgnoreCase: term } }
|
|
24631
|
+
];
|
|
24632
|
+
const identifierMatch = term.match(/^[A-Za-z][\w]*-(\d+)$/);
|
|
24633
|
+
const asNumber = identifierMatch ? Number(identifierMatch[1]) : /^\d+$/.test(term) ? Number(term) : NaN;
|
|
24634
|
+
if (Number.isFinite(asNumber) && asNumber > 0) {
|
|
24635
|
+
or.push({ number: { eq: asNumber } });
|
|
24636
|
+
}
|
|
24637
|
+
filter.or = or;
|
|
24638
|
+
}
|
|
24625
24639
|
function buildLinearIssueFilter(input) {
|
|
24626
24640
|
const filter = {
|
|
24627
24641
|
state: { type: { nin: ["completed", "canceled"] } }
|
|
24628
24642
|
};
|
|
24629
24643
|
const since = input?.updatedSince?.trim();
|
|
24630
24644
|
if (since) filter.updatedAt = { gte: since };
|
|
24645
|
+
applyLinearIssueQueryFilter(filter, input?.query);
|
|
24631
24646
|
const raw = assigneeFilterKey(input?.assignee) || "me";
|
|
24632
24647
|
const key = raw.toLowerCase();
|
|
24633
24648
|
if (key === "all" || key === "*") return filter;
|
|
@@ -24693,6 +24708,11 @@ async function listLinearIssuesFiltered(opts) {
|
|
|
24693
24708
|
async function listLinearCommentsSince(opts) {
|
|
24694
24709
|
const first = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
24695
24710
|
const query = opts.query?.trim() ?? "";
|
|
24711
|
+
const restrictToIssues = opts.issueIdentifiers != null;
|
|
24712
|
+
const allowed = new Set(
|
|
24713
|
+
(opts.issueIdentifiers ?? []).map((id) => id.trim()).filter(Boolean)
|
|
24714
|
+
);
|
|
24715
|
+
if (restrictToIssues && allowed.size === 0) return [];
|
|
24696
24716
|
const assignee = assigneeFilterKey(opts.assignee) || (query ? "all" : "me");
|
|
24697
24717
|
const json = await linearGraphql(
|
|
24698
24718
|
COMMENTS_SINCE_QUERY,
|
|
@@ -24700,7 +24720,7 @@ async function listLinearCommentsSince(opts) {
|
|
|
24700
24720
|
first,
|
|
24701
24721
|
filter: {
|
|
24702
24722
|
createdAt: { gte: opts.since },
|
|
24703
|
-
issue: buildLinearIssueFilter({ assignee })
|
|
24723
|
+
issue: buildLinearIssueFilter({ assignee, query: query || void 0 })
|
|
24704
24724
|
}
|
|
24705
24725
|
},
|
|
24706
24726
|
opts
|
|
@@ -24708,6 +24728,7 @@ async function listLinearCommentsSince(opts) {
|
|
|
24708
24728
|
const out = [];
|
|
24709
24729
|
for (const node of json.comments?.nodes ?? []) {
|
|
24710
24730
|
const identifier = String(node.issue?.identifier ?? "").trim();
|
|
24731
|
+
if (restrictToIssues && !allowed.has(identifier)) continue;
|
|
24711
24732
|
const body = previewIssueCommentBody(String(node.body ?? ""));
|
|
24712
24733
|
if (!identifier && !body) continue;
|
|
24713
24734
|
out.push({
|
|
@@ -24939,10 +24960,32 @@ function issueNumberFromApiUrl(url) {
|
|
|
24939
24960
|
const number = Number(match[1]);
|
|
24940
24961
|
return Number.isFinite(number) && number > 0 ? number : null;
|
|
24941
24962
|
}
|
|
24963
|
+
var GITHUB_ISSUE_COMMENTS_PAGE_SIZE = 100;
|
|
24964
|
+
var GITHUB_ISSUE_COMMENTS_MAX_PAGES = 5;
|
|
24965
|
+
function parseGitHubIssueCommentNode(item, allowed) {
|
|
24966
|
+
const rec = item && typeof item === "object" ? item : null;
|
|
24967
|
+
if (!rec) return null;
|
|
24968
|
+
const issueUrl = typeof rec.issue_url === "string" ? rec.issue_url : "";
|
|
24969
|
+
const number = issueNumberFromApiUrl(issueUrl);
|
|
24970
|
+
if (number == null) return null;
|
|
24971
|
+
if (allowed && !allowed.has(number)) return null;
|
|
24972
|
+
const body = previewIssueCommentBody(typeof rec.body === "string" ? rec.body : "");
|
|
24973
|
+
const user = rec.user && typeof rec.user === "object" ? String(rec.user.login ?? "").trim() : "";
|
|
24974
|
+
const createdAt = typeof rec.created_at === "string" ? rec.created_at : typeof rec.createdAt === "string" ? rec.createdAt : void 0;
|
|
24975
|
+
const url = typeof rec.html_url === "string" ? rec.html_url : void 0;
|
|
24976
|
+
return {
|
|
24977
|
+
identifier: `#${number}`,
|
|
24978
|
+
...user ? { author: user } : {},
|
|
24979
|
+
...createdAt ? { createdAt } : {},
|
|
24980
|
+
body,
|
|
24981
|
+
...url ? { url } : {}
|
|
24982
|
+
};
|
|
24983
|
+
}
|
|
24942
24984
|
async function listGitHubIssueCommentsSince(opts) {
|
|
24943
24985
|
const limit = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
24944
24986
|
const { cwd, slug } = await resolveGitHubIssueRepo(opts.repoPath);
|
|
24945
24987
|
if (!slug) return [];
|
|
24988
|
+
const restrictToIssues = opts.issueIdentifiers != null;
|
|
24946
24989
|
const allowed = new Set(
|
|
24947
24990
|
(opts.issueIdentifiers ?? []).map((id) => {
|
|
24948
24991
|
try {
|
|
@@ -24952,36 +24995,27 @@ async function listGitHubIssueCommentsSince(opts) {
|
|
|
24952
24995
|
}
|
|
24953
24996
|
}).filter((n) => n != null)
|
|
24954
24997
|
);
|
|
24955
|
-
|
|
24956
|
-
const
|
|
24957
|
-
if (result.exitCode !== 0 || !result.stdout.trim()) return [];
|
|
24958
|
-
let parsed;
|
|
24959
|
-
try {
|
|
24960
|
-
parsed = JSON.parse(result.stdout);
|
|
24961
|
-
} catch {
|
|
24962
|
-
return [];
|
|
24963
|
-
}
|
|
24964
|
-
if (!Array.isArray(parsed)) return [];
|
|
24998
|
+
if (restrictToIssues && allowed.size === 0) return [];
|
|
24999
|
+
const pageSize = Math.min(GITHUB_ISSUE_COMMENTS_PAGE_SIZE, Math.max(1, limit));
|
|
24965
25000
|
const out = [];
|
|
24966
|
-
for (
|
|
24967
|
-
const
|
|
24968
|
-
|
|
24969
|
-
|
|
24970
|
-
|
|
24971
|
-
|
|
24972
|
-
|
|
24973
|
-
|
|
24974
|
-
|
|
24975
|
-
|
|
24976
|
-
|
|
24977
|
-
|
|
24978
|
-
|
|
24979
|
-
|
|
24980
|
-
|
|
24981
|
-
|
|
24982
|
-
|
|
24983
|
-
|
|
24984
|
-
if (out.length >= limit) break;
|
|
25001
|
+
for (let page = 1; page <= GITHUB_ISSUE_COMMENTS_MAX_PAGES; page++) {
|
|
25002
|
+
const path2 = `repos/${slug}/issues/comments?since=${encodeURIComponent(opts.since)}&sort=created&direction=desc&per_page=${pageSize}&page=${page}`;
|
|
25003
|
+
const result = await gh(["api", path2], cwd, { reject: false });
|
|
25004
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) break;
|
|
25005
|
+
let parsed;
|
|
25006
|
+
try {
|
|
25007
|
+
parsed = JSON.parse(result.stdout);
|
|
25008
|
+
} catch {
|
|
25009
|
+
break;
|
|
25010
|
+
}
|
|
25011
|
+
if (!Array.isArray(parsed) || parsed.length === 0) break;
|
|
25012
|
+
for (const item of parsed) {
|
|
25013
|
+
const comment = parseGitHubIssueCommentNode(item, restrictToIssues ? allowed : null);
|
|
25014
|
+
if (!comment) continue;
|
|
25015
|
+
out.push(comment);
|
|
25016
|
+
if (out.length >= limit) return out;
|
|
25017
|
+
}
|
|
25018
|
+
if (parsed.length < pageSize) break;
|
|
24985
25019
|
}
|
|
24986
25020
|
return out;
|
|
24987
25021
|
}
|
|
@@ -25195,20 +25229,19 @@ async function listIssues(repoPath, opts) {
|
|
|
25195
25229
|
const assignee = opts?.assignee?.trim() || void 0;
|
|
25196
25230
|
const since = opts?.updatedSince ? parseIssueSince(opts.updatedSince) : void 0;
|
|
25197
25231
|
if (source === "linear") {
|
|
25198
|
-
const
|
|
25199
|
-
|
|
25200
|
-
|
|
25201
|
-
|
|
25202
|
-
|
|
25203
|
-
|
|
25204
|
-
|
|
25205
|
-
since
|
|
25206
|
-
|
|
25207
|
-
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
]);
|
|
25232
|
+
const listed = await listLinearIssuesFiltered({
|
|
25233
|
+
assignee,
|
|
25234
|
+
query,
|
|
25235
|
+
limit: opts?.limit,
|
|
25236
|
+
updatedSince: since
|
|
25237
|
+
});
|
|
25238
|
+
const comments2 = since ? await listLinearCommentsSince({
|
|
25239
|
+
since,
|
|
25240
|
+
assignee,
|
|
25241
|
+
query,
|
|
25242
|
+
issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
|
|
25243
|
+
limit: opts?.limit
|
|
25244
|
+
}) : void 0;
|
|
25212
25245
|
return {
|
|
25213
25246
|
source,
|
|
25214
25247
|
preferredSource,
|
|
@@ -26955,20 +26988,19 @@ function registerLinearTools(server) {
|
|
|
26955
26988
|
try {
|
|
26956
26989
|
const page = clampMcpIssueLimit(limit);
|
|
26957
26990
|
const since = updatedSince ? parseIssueSince(updatedSince) : void 0;
|
|
26958
|
-
const
|
|
26959
|
-
|
|
26960
|
-
|
|
26961
|
-
|
|
26962
|
-
|
|
26963
|
-
|
|
26964
|
-
|
|
26965
|
-
since
|
|
26966
|
-
|
|
26967
|
-
|
|
26968
|
-
|
|
26969
|
-
|
|
26970
|
-
|
|
26971
|
-
]);
|
|
26991
|
+
const listed = await listLinearIssuesFiltered({
|
|
26992
|
+
query,
|
|
26993
|
+
assignee,
|
|
26994
|
+
limit: page + 1,
|
|
26995
|
+
updatedSince: since
|
|
26996
|
+
});
|
|
26997
|
+
const comments = since ? await listLinearCommentsSince({
|
|
26998
|
+
since,
|
|
26999
|
+
assignee,
|
|
27000
|
+
query,
|
|
27001
|
+
issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
|
|
27002
|
+
limit: page + 1
|
|
27003
|
+
}) : void 0;
|
|
26972
27004
|
return mcpJson(
|
|
26973
27005
|
formatListedIssuesForMcp(
|
|
26974
27006
|
{
|
package/dist/index.d.cts
CHANGED
|
@@ -2213,13 +2213,10 @@ type LinearAssignedIssuesResult = {
|
|
|
2213
2213
|
};
|
|
2214
2214
|
/** `me`, `unassigned`, `all`, a Linear user id, or a display name. */
|
|
2215
2215
|
type LinearAssigneeFilter = string;
|
|
2216
|
-
/**
|
|
2217
|
-
* Linear `IssueFilter` for open issues. Default assignee is `me` so existing
|
|
2218
|
-
* Home / Create-from callers stay assigned-to-you unless they ask otherwise.
|
|
2219
|
-
*/
|
|
2220
2216
|
declare function buildLinearIssueFilter(input?: {
|
|
2221
2217
|
assignee?: string | null;
|
|
2222
2218
|
updatedSince?: string | null;
|
|
2219
|
+
query?: string | null;
|
|
2223
2220
|
}): Record<string, unknown>;
|
|
2224
2221
|
/**
|
|
2225
2222
|
* List or search open Linear issues. `assignee` is `me` (default when there is
|
|
@@ -2234,13 +2231,16 @@ declare function listLinearIssuesFiltered(opts?: {
|
|
|
2234
2231
|
updatedSince?: string | null;
|
|
2235
2232
|
}): Promise<LinearAssignedIssuesResult>;
|
|
2236
2233
|
/**
|
|
2237
|
-
* Comments created at/after `since` on issues matching the same assignee
|
|
2238
|
-
* One GraphQL query — do not get_issue each ticket to check for new comments.
|
|
2234
|
+
* Comments created at/after `since` on issues matching the same assignee / search
|
|
2235
|
+
* filter. One GraphQL query — do not get_issue each ticket to check for new comments.
|
|
2236
|
+
* Pass `issueIdentifiers` (search hits) to keep only those tickets; an empty
|
|
2237
|
+
* allowlist means no comments.
|
|
2239
2238
|
*/
|
|
2240
2239
|
declare function listLinearCommentsSince(opts: {
|
|
2241
2240
|
since: string;
|
|
2242
2241
|
assignee?: string | null;
|
|
2243
2242
|
query?: string;
|
|
2243
|
+
issueIdentifiers?: string[];
|
|
2244
2244
|
limit?: number;
|
|
2245
2245
|
apiKey?: string | null;
|
|
2246
2246
|
}): Promise<IssueActivityComment[]>;
|
|
@@ -2364,7 +2364,8 @@ declare function resolveGitHubIssueRepo(repoPath?: string | null): Promise<{
|
|
|
2364
2364
|
declare function toGitHubIssueInfo(issue: GitHubIssue): IssueInfo;
|
|
2365
2365
|
/**
|
|
2366
2366
|
* Repo issue comments created at/after `since`. Pass `issueIdentifiers` (#12)
|
|
2367
|
-
* to keep only comments on that inbox page —
|
|
2367
|
+
* to keep only comments on that inbox page — paginated REST, not N issue views.
|
|
2368
|
+
* An empty allowlist means no comments (do not fall through to the whole repo).
|
|
2368
2369
|
*/
|
|
2369
2370
|
declare function listGitHubIssueCommentsSince(opts: {
|
|
2370
2371
|
since: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -2213,13 +2213,10 @@ type LinearAssignedIssuesResult = {
|
|
|
2213
2213
|
};
|
|
2214
2214
|
/** `me`, `unassigned`, `all`, a Linear user id, or a display name. */
|
|
2215
2215
|
type LinearAssigneeFilter = string;
|
|
2216
|
-
/**
|
|
2217
|
-
* Linear `IssueFilter` for open issues. Default assignee is `me` so existing
|
|
2218
|
-
* Home / Create-from callers stay assigned-to-you unless they ask otherwise.
|
|
2219
|
-
*/
|
|
2220
2216
|
declare function buildLinearIssueFilter(input?: {
|
|
2221
2217
|
assignee?: string | null;
|
|
2222
2218
|
updatedSince?: string | null;
|
|
2219
|
+
query?: string | null;
|
|
2223
2220
|
}): Record<string, unknown>;
|
|
2224
2221
|
/**
|
|
2225
2222
|
* List or search open Linear issues. `assignee` is `me` (default when there is
|
|
@@ -2234,13 +2231,16 @@ declare function listLinearIssuesFiltered(opts?: {
|
|
|
2234
2231
|
updatedSince?: string | null;
|
|
2235
2232
|
}): Promise<LinearAssignedIssuesResult>;
|
|
2236
2233
|
/**
|
|
2237
|
-
* Comments created at/after `since` on issues matching the same assignee
|
|
2238
|
-
* One GraphQL query — do not get_issue each ticket to check for new comments.
|
|
2234
|
+
* Comments created at/after `since` on issues matching the same assignee / search
|
|
2235
|
+
* filter. One GraphQL query — do not get_issue each ticket to check for new comments.
|
|
2236
|
+
* Pass `issueIdentifiers` (search hits) to keep only those tickets; an empty
|
|
2237
|
+
* allowlist means no comments.
|
|
2239
2238
|
*/
|
|
2240
2239
|
declare function listLinearCommentsSince(opts: {
|
|
2241
2240
|
since: string;
|
|
2242
2241
|
assignee?: string | null;
|
|
2243
2242
|
query?: string;
|
|
2243
|
+
issueIdentifiers?: string[];
|
|
2244
2244
|
limit?: number;
|
|
2245
2245
|
apiKey?: string | null;
|
|
2246
2246
|
}): Promise<IssueActivityComment[]>;
|
|
@@ -2364,7 +2364,8 @@ declare function resolveGitHubIssueRepo(repoPath?: string | null): Promise<{
|
|
|
2364
2364
|
declare function toGitHubIssueInfo(issue: GitHubIssue): IssueInfo;
|
|
2365
2365
|
/**
|
|
2366
2366
|
* Repo issue comments created at/after `since`. Pass `issueIdentifiers` (#12)
|
|
2367
|
-
* to keep only comments on that inbox page —
|
|
2367
|
+
* to keep only comments on that inbox page — paginated REST, not N issue views.
|
|
2368
|
+
* An empty allowlist means no comments (do not fall through to the whole repo).
|
|
2368
2369
|
*/
|
|
2369
2370
|
declare function listGitHubIssueCommentsSince(opts: {
|
|
2370
2371
|
since: string;
|
package/dist/index.js
CHANGED
|
@@ -238,7 +238,7 @@ import {
|
|
|
238
238
|
worktreeCleanupSettings,
|
|
239
239
|
wrapReviewSkillMarkdown,
|
|
240
240
|
writeWorktreeFile
|
|
241
|
-
} from "./chunk-
|
|
241
|
+
} from "./chunk-G4VAMUCL.js";
|
|
242
242
|
import {
|
|
243
243
|
LEGACY_PLAN_FILE_REL,
|
|
244
244
|
PLAN_FILE_NAME,
|
|
@@ -1518,12 +1518,27 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
1518
1518
|
function assigneeFilterKey(assignee) {
|
|
1519
1519
|
return (assignee ?? "").trim();
|
|
1520
1520
|
}
|
|
1521
|
+
function applyLinearIssueQueryFilter(filter, query) {
|
|
1522
|
+
const term = query?.trim();
|
|
1523
|
+
if (!term) return;
|
|
1524
|
+
const or = [
|
|
1525
|
+
{ title: { containsIgnoreCase: term } },
|
|
1526
|
+
{ description: { containsIgnoreCase: term } }
|
|
1527
|
+
];
|
|
1528
|
+
const identifierMatch = term.match(/^[A-Za-z][\w]*-(\d+)$/);
|
|
1529
|
+
const asNumber = identifierMatch ? Number(identifierMatch[1]) : /^\d+$/.test(term) ? Number(term) : NaN;
|
|
1530
|
+
if (Number.isFinite(asNumber) && asNumber > 0) {
|
|
1531
|
+
or.push({ number: { eq: asNumber } });
|
|
1532
|
+
}
|
|
1533
|
+
filter.or = or;
|
|
1534
|
+
}
|
|
1521
1535
|
function buildLinearIssueFilter(input) {
|
|
1522
1536
|
const filter = {
|
|
1523
1537
|
state: { type: { nin: ["completed", "canceled"] } }
|
|
1524
1538
|
};
|
|
1525
1539
|
const since = input?.updatedSince?.trim();
|
|
1526
1540
|
if (since) filter.updatedAt = { gte: since };
|
|
1541
|
+
applyLinearIssueQueryFilter(filter, input?.query);
|
|
1527
1542
|
const raw = assigneeFilterKey(input?.assignee) || "me";
|
|
1528
1543
|
const key = raw.toLowerCase();
|
|
1529
1544
|
if (key === "all" || key === "*") return filter;
|
|
@@ -1589,6 +1604,11 @@ async function listLinearIssuesFiltered(opts) {
|
|
|
1589
1604
|
async function listLinearCommentsSince(opts) {
|
|
1590
1605
|
const first = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
1591
1606
|
const query = opts.query?.trim() ?? "";
|
|
1607
|
+
const restrictToIssues = opts.issueIdentifiers != null;
|
|
1608
|
+
const allowed = new Set(
|
|
1609
|
+
(opts.issueIdentifiers ?? []).map((id) => id.trim()).filter(Boolean)
|
|
1610
|
+
);
|
|
1611
|
+
if (restrictToIssues && allowed.size === 0) return [];
|
|
1592
1612
|
const assignee = assigneeFilterKey(opts.assignee) || (query ? "all" : "me");
|
|
1593
1613
|
const json = await linearGraphql(
|
|
1594
1614
|
COMMENTS_SINCE_QUERY,
|
|
@@ -1596,7 +1616,7 @@ async function listLinearCommentsSince(opts) {
|
|
|
1596
1616
|
first,
|
|
1597
1617
|
filter: {
|
|
1598
1618
|
createdAt: { gte: opts.since },
|
|
1599
|
-
issue: buildLinearIssueFilter({ assignee })
|
|
1619
|
+
issue: buildLinearIssueFilter({ assignee, query: query || void 0 })
|
|
1600
1620
|
}
|
|
1601
1621
|
},
|
|
1602
1622
|
opts
|
|
@@ -1604,6 +1624,7 @@ async function listLinearCommentsSince(opts) {
|
|
|
1604
1624
|
const out = [];
|
|
1605
1625
|
for (const node of json.comments?.nodes ?? []) {
|
|
1606
1626
|
const identifier = String(node.issue?.identifier ?? "").trim();
|
|
1627
|
+
if (restrictToIssues && !allowed.has(identifier)) continue;
|
|
1607
1628
|
const body = previewIssueCommentBody(String(node.body ?? ""));
|
|
1608
1629
|
if (!identifier && !body) continue;
|
|
1609
1630
|
out.push({
|
|
@@ -1833,10 +1854,32 @@ function issueNumberFromApiUrl(url) {
|
|
|
1833
1854
|
const number = Number(match[1]);
|
|
1834
1855
|
return Number.isFinite(number) && number > 0 ? number : null;
|
|
1835
1856
|
}
|
|
1857
|
+
var GITHUB_ISSUE_COMMENTS_PAGE_SIZE = 100;
|
|
1858
|
+
var GITHUB_ISSUE_COMMENTS_MAX_PAGES = 5;
|
|
1859
|
+
function parseGitHubIssueCommentNode(item, allowed) {
|
|
1860
|
+
const rec = item && typeof item === "object" ? item : null;
|
|
1861
|
+
if (!rec) return null;
|
|
1862
|
+
const issueUrl = typeof rec.issue_url === "string" ? rec.issue_url : "";
|
|
1863
|
+
const number = issueNumberFromApiUrl(issueUrl);
|
|
1864
|
+
if (number == null) return null;
|
|
1865
|
+
if (allowed && !allowed.has(number)) return null;
|
|
1866
|
+
const body = previewIssueCommentBody(typeof rec.body === "string" ? rec.body : "");
|
|
1867
|
+
const user = rec.user && typeof rec.user === "object" ? String(rec.user.login ?? "").trim() : "";
|
|
1868
|
+
const createdAt = typeof rec.created_at === "string" ? rec.created_at : typeof rec.createdAt === "string" ? rec.createdAt : void 0;
|
|
1869
|
+
const url = typeof rec.html_url === "string" ? rec.html_url : void 0;
|
|
1870
|
+
return {
|
|
1871
|
+
identifier: `#${number}`,
|
|
1872
|
+
...user ? { author: user } : {},
|
|
1873
|
+
...createdAt ? { createdAt } : {},
|
|
1874
|
+
body,
|
|
1875
|
+
...url ? { url } : {}
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1836
1878
|
async function listGitHubIssueCommentsSince(opts) {
|
|
1837
1879
|
const limit = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
1838
1880
|
const { cwd, slug } = await resolveGitHubIssueRepo(opts.repoPath);
|
|
1839
1881
|
if (!slug) return [];
|
|
1882
|
+
const restrictToIssues = opts.issueIdentifiers != null;
|
|
1840
1883
|
const allowed = new Set(
|
|
1841
1884
|
(opts.issueIdentifiers ?? []).map((id) => {
|
|
1842
1885
|
try {
|
|
@@ -1846,36 +1889,27 @@ async function listGitHubIssueCommentsSince(opts) {
|
|
|
1846
1889
|
}
|
|
1847
1890
|
}).filter((n) => n != null)
|
|
1848
1891
|
);
|
|
1849
|
-
|
|
1850
|
-
const
|
|
1851
|
-
if (result.exitCode !== 0 || !result.stdout.trim()) return [];
|
|
1852
|
-
let parsed;
|
|
1853
|
-
try {
|
|
1854
|
-
parsed = JSON.parse(result.stdout);
|
|
1855
|
-
} catch {
|
|
1856
|
-
return [];
|
|
1857
|
-
}
|
|
1858
|
-
if (!Array.isArray(parsed)) return [];
|
|
1892
|
+
if (restrictToIssues && allowed.size === 0) return [];
|
|
1893
|
+
const pageSize = Math.min(GITHUB_ISSUE_COMMENTS_PAGE_SIZE, Math.max(1, limit));
|
|
1859
1894
|
const out = [];
|
|
1860
|
-
for (
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
if (out.length >= limit) break;
|
|
1895
|
+
for (let page = 1; page <= GITHUB_ISSUE_COMMENTS_MAX_PAGES; page++) {
|
|
1896
|
+
const path2 = `repos/${slug}/issues/comments?since=${encodeURIComponent(opts.since)}&sort=created&direction=desc&per_page=${pageSize}&page=${page}`;
|
|
1897
|
+
const result = await gh(["api", path2], cwd, { reject: false });
|
|
1898
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) break;
|
|
1899
|
+
let parsed;
|
|
1900
|
+
try {
|
|
1901
|
+
parsed = JSON.parse(result.stdout);
|
|
1902
|
+
} catch {
|
|
1903
|
+
break;
|
|
1904
|
+
}
|
|
1905
|
+
if (!Array.isArray(parsed) || parsed.length === 0) break;
|
|
1906
|
+
for (const item of parsed) {
|
|
1907
|
+
const comment = parseGitHubIssueCommentNode(item, restrictToIssues ? allowed : null);
|
|
1908
|
+
if (!comment) continue;
|
|
1909
|
+
out.push(comment);
|
|
1910
|
+
if (out.length >= limit) return out;
|
|
1911
|
+
}
|
|
1912
|
+
if (parsed.length < pageSize) break;
|
|
1879
1913
|
}
|
|
1880
1914
|
return out;
|
|
1881
1915
|
}
|
|
@@ -2081,20 +2115,19 @@ async function listIssues(repoPath, opts) {
|
|
|
2081
2115
|
const assignee = opts?.assignee?.trim() || void 0;
|
|
2082
2116
|
const since = opts?.updatedSince ? parseIssueSince(opts.updatedSince) : void 0;
|
|
2083
2117
|
if (source === "linear") {
|
|
2084
|
-
const
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
since
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
]);
|
|
2118
|
+
const listed = await listLinearIssuesFiltered({
|
|
2119
|
+
assignee,
|
|
2120
|
+
query,
|
|
2121
|
+
limit: opts?.limit,
|
|
2122
|
+
updatedSince: since
|
|
2123
|
+
});
|
|
2124
|
+
const comments2 = since ? await listLinearCommentsSince({
|
|
2125
|
+
since,
|
|
2126
|
+
assignee,
|
|
2127
|
+
query,
|
|
2128
|
+
issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
|
|
2129
|
+
limit: opts?.limit
|
|
2130
|
+
}) : void 0;
|
|
2098
2131
|
return {
|
|
2099
2132
|
source,
|
|
2100
2133
|
preferredSource,
|
|
@@ -3762,20 +3795,19 @@ function registerLinearTools(server) {
|
|
|
3762
3795
|
try {
|
|
3763
3796
|
const page = clampMcpIssueLimit(limit);
|
|
3764
3797
|
const since = updatedSince ? parseIssueSince(updatedSince) : void 0;
|
|
3765
|
-
const
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
since
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
]);
|
|
3798
|
+
const listed = await listLinearIssuesFiltered({
|
|
3799
|
+
query,
|
|
3800
|
+
assignee,
|
|
3801
|
+
limit: page + 1,
|
|
3802
|
+
updatedSince: since
|
|
3803
|
+
});
|
|
3804
|
+
const comments = since ? await listLinearCommentsSince({
|
|
3805
|
+
since,
|
|
3806
|
+
assignee,
|
|
3807
|
+
query,
|
|
3808
|
+
issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
|
|
3809
|
+
limit: page + 1
|
|
3810
|
+
}) : void 0;
|
|
3779
3811
|
return mcpJson(
|
|
3780
3812
|
formatListedIssuesForMcp(
|
|
3781
3813
|
{
|
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -14694,7 +14694,7 @@ function classifyWorktreeOwnership(group, viewerLogin = "") {
|
|
|
14694
14694
|
const reviewing = group.some((t) => {
|
|
14695
14695
|
const author = normalizeViewerLogin(t.prAuthorLogin);
|
|
14696
14696
|
if (author && me && author !== me) return true;
|
|
14697
|
-
if (t.sourceType === "pr" && (!author ||
|
|
14697
|
+
if (t.sourceType === "pr" && me && (!author || author !== me)) return true;
|
|
14698
14698
|
if (me && loginsInclude(t.prReviewerLogins, me)) return true;
|
|
14699
14699
|
return false;
|
|
14700
14700
|
});
|
|
@@ -21709,10 +21709,32 @@ function issueNumberFromApiUrl(url) {
|
|
|
21709
21709
|
const number = Number(match[1]);
|
|
21710
21710
|
return Number.isFinite(number) && number > 0 ? number : null;
|
|
21711
21711
|
}
|
|
21712
|
+
var GITHUB_ISSUE_COMMENTS_PAGE_SIZE = 100;
|
|
21713
|
+
var GITHUB_ISSUE_COMMENTS_MAX_PAGES = 5;
|
|
21714
|
+
function parseGitHubIssueCommentNode(item, allowed) {
|
|
21715
|
+
const rec = item && typeof item === "object" ? item : null;
|
|
21716
|
+
if (!rec) return null;
|
|
21717
|
+
const issueUrl = typeof rec.issue_url === "string" ? rec.issue_url : "";
|
|
21718
|
+
const number = issueNumberFromApiUrl(issueUrl);
|
|
21719
|
+
if (number == null) return null;
|
|
21720
|
+
if (allowed && !allowed.has(number)) return null;
|
|
21721
|
+
const body = previewIssueCommentBody(typeof rec.body === "string" ? rec.body : "");
|
|
21722
|
+
const user = rec.user && typeof rec.user === "object" ? String(rec.user.login ?? "").trim() : "";
|
|
21723
|
+
const createdAt = typeof rec.created_at === "string" ? rec.created_at : typeof rec.createdAt === "string" ? rec.createdAt : void 0;
|
|
21724
|
+
const url = typeof rec.html_url === "string" ? rec.html_url : void 0;
|
|
21725
|
+
return {
|
|
21726
|
+
identifier: `#${number}`,
|
|
21727
|
+
...user ? { author: user } : {},
|
|
21728
|
+
...createdAt ? { createdAt } : {},
|
|
21729
|
+
body,
|
|
21730
|
+
...url ? { url } : {}
|
|
21731
|
+
};
|
|
21732
|
+
}
|
|
21712
21733
|
async function listGitHubIssueCommentsSince(opts) {
|
|
21713
21734
|
const limit = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
21714
21735
|
const { cwd, slug } = await resolveGitHubIssueRepo(opts.repoPath);
|
|
21715
21736
|
if (!slug) return [];
|
|
21737
|
+
const restrictToIssues = opts.issueIdentifiers != null;
|
|
21716
21738
|
const allowed = new Set(
|
|
21717
21739
|
(opts.issueIdentifiers ?? []).map((id) => {
|
|
21718
21740
|
try {
|
|
@@ -21722,36 +21744,27 @@ async function listGitHubIssueCommentsSince(opts) {
|
|
|
21722
21744
|
}
|
|
21723
21745
|
}).filter((n) => n != null)
|
|
21724
21746
|
);
|
|
21725
|
-
|
|
21726
|
-
const
|
|
21727
|
-
if (result.exitCode !== 0 || !result.stdout.trim()) return [];
|
|
21728
|
-
let parsed;
|
|
21729
|
-
try {
|
|
21730
|
-
parsed = JSON.parse(result.stdout);
|
|
21731
|
-
} catch {
|
|
21732
|
-
return [];
|
|
21733
|
-
}
|
|
21734
|
-
if (!Array.isArray(parsed)) return [];
|
|
21747
|
+
if (restrictToIssues && allowed.size === 0) return [];
|
|
21748
|
+
const pageSize = Math.min(GITHUB_ISSUE_COMMENTS_PAGE_SIZE, Math.max(1, limit));
|
|
21735
21749
|
const out = [];
|
|
21736
|
-
for (
|
|
21737
|
-
const
|
|
21738
|
-
|
|
21739
|
-
|
|
21740
|
-
|
|
21741
|
-
|
|
21742
|
-
|
|
21743
|
-
|
|
21744
|
-
|
|
21745
|
-
|
|
21746
|
-
|
|
21747
|
-
|
|
21748
|
-
|
|
21749
|
-
|
|
21750
|
-
|
|
21751
|
-
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
if (out.length >= limit) break;
|
|
21750
|
+
for (let page = 1; page <= GITHUB_ISSUE_COMMENTS_MAX_PAGES; page++) {
|
|
21751
|
+
const path = `repos/${slug}/issues/comments?since=${encodeURIComponent(opts.since)}&sort=created&direction=desc&per_page=${pageSize}&page=${page}`;
|
|
21752
|
+
const result = await gh(["api", path], cwd, { reject: false });
|
|
21753
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) break;
|
|
21754
|
+
let parsed;
|
|
21755
|
+
try {
|
|
21756
|
+
parsed = JSON.parse(result.stdout);
|
|
21757
|
+
} catch {
|
|
21758
|
+
break;
|
|
21759
|
+
}
|
|
21760
|
+
if (!Array.isArray(parsed) || parsed.length === 0) break;
|
|
21761
|
+
for (const item of parsed) {
|
|
21762
|
+
const comment = parseGitHubIssueCommentNode(item, restrictToIssues ? allowed : null);
|
|
21763
|
+
if (!comment) continue;
|
|
21764
|
+
out.push(comment);
|
|
21765
|
+
if (out.length >= limit) return out;
|
|
21766
|
+
}
|
|
21767
|
+
if (parsed.length < pageSize) break;
|
|
21755
21768
|
}
|
|
21756
21769
|
return out;
|
|
21757
21770
|
}
|
|
@@ -22361,12 +22374,27 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
22361
22374
|
function assigneeFilterKey(assignee) {
|
|
22362
22375
|
return (assignee ?? "").trim();
|
|
22363
22376
|
}
|
|
22377
|
+
function applyLinearIssueQueryFilter(filter, query) {
|
|
22378
|
+
const term = query?.trim();
|
|
22379
|
+
if (!term) return;
|
|
22380
|
+
const or = [
|
|
22381
|
+
{ title: { containsIgnoreCase: term } },
|
|
22382
|
+
{ description: { containsIgnoreCase: term } }
|
|
22383
|
+
];
|
|
22384
|
+
const identifierMatch = term.match(/^[A-Za-z][\w]*-(\d+)$/);
|
|
22385
|
+
const asNumber = identifierMatch ? Number(identifierMatch[1]) : /^\d+$/.test(term) ? Number(term) : NaN;
|
|
22386
|
+
if (Number.isFinite(asNumber) && asNumber > 0) {
|
|
22387
|
+
or.push({ number: { eq: asNumber } });
|
|
22388
|
+
}
|
|
22389
|
+
filter.or = or;
|
|
22390
|
+
}
|
|
22364
22391
|
function buildLinearIssueFilter(input) {
|
|
22365
22392
|
const filter = {
|
|
22366
22393
|
state: { type: { nin: ["completed", "canceled"] } }
|
|
22367
22394
|
};
|
|
22368
22395
|
const since = input?.updatedSince?.trim();
|
|
22369
22396
|
if (since) filter.updatedAt = { gte: since };
|
|
22397
|
+
applyLinearIssueQueryFilter(filter, input?.query);
|
|
22370
22398
|
const raw = assigneeFilterKey(input?.assignee) || "me";
|
|
22371
22399
|
const key = raw.toLowerCase();
|
|
22372
22400
|
if (key === "all" || key === "*") return filter;
|
|
@@ -22432,6 +22460,11 @@ async function listLinearIssuesFiltered(opts) {
|
|
|
22432
22460
|
async function listLinearCommentsSince(opts) {
|
|
22433
22461
|
const first = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
22434
22462
|
const query = opts.query?.trim() ?? "";
|
|
22463
|
+
const restrictToIssues = opts.issueIdentifiers != null;
|
|
22464
|
+
const allowed = new Set(
|
|
22465
|
+
(opts.issueIdentifiers ?? []).map((id) => id.trim()).filter(Boolean)
|
|
22466
|
+
);
|
|
22467
|
+
if (restrictToIssues && allowed.size === 0) return [];
|
|
22435
22468
|
const assignee = assigneeFilterKey(opts.assignee) || (query ? "all" : "me");
|
|
22436
22469
|
const json = await linearGraphql(
|
|
22437
22470
|
COMMENTS_SINCE_QUERY,
|
|
@@ -22439,7 +22472,7 @@ async function listLinearCommentsSince(opts) {
|
|
|
22439
22472
|
first,
|
|
22440
22473
|
filter: {
|
|
22441
22474
|
createdAt: { gte: opts.since },
|
|
22442
|
-
issue: buildLinearIssueFilter({ assignee })
|
|
22475
|
+
issue: buildLinearIssueFilter({ assignee, query: query || void 0 })
|
|
22443
22476
|
}
|
|
22444
22477
|
},
|
|
22445
22478
|
opts
|
|
@@ -22447,6 +22480,7 @@ async function listLinearCommentsSince(opts) {
|
|
|
22447
22480
|
const out = [];
|
|
22448
22481
|
for (const node of json.comments?.nodes ?? []) {
|
|
22449
22482
|
const identifier = String(node.issue?.identifier ?? "").trim();
|
|
22483
|
+
if (restrictToIssues && !allowed.has(identifier)) continue;
|
|
22450
22484
|
const body = previewIssueCommentBody(String(node.body ?? ""));
|
|
22451
22485
|
if (!identifier && !body) continue;
|
|
22452
22486
|
out.push({
|
|
@@ -22672,20 +22706,19 @@ async function listIssues(repoPath, opts) {
|
|
|
22672
22706
|
const assignee = opts?.assignee?.trim() || void 0;
|
|
22673
22707
|
const since = opts?.updatedSince ? parseIssueSince(opts.updatedSince) : void 0;
|
|
22674
22708
|
if (source === "linear") {
|
|
22675
|
-
const
|
|
22676
|
-
|
|
22677
|
-
|
|
22678
|
-
|
|
22679
|
-
|
|
22680
|
-
|
|
22681
|
-
|
|
22682
|
-
since
|
|
22683
|
-
|
|
22684
|
-
|
|
22685
|
-
|
|
22686
|
-
|
|
22687
|
-
|
|
22688
|
-
]);
|
|
22709
|
+
const listed = await listLinearIssuesFiltered({
|
|
22710
|
+
assignee,
|
|
22711
|
+
query,
|
|
22712
|
+
limit: opts?.limit,
|
|
22713
|
+
updatedSince: since
|
|
22714
|
+
});
|
|
22715
|
+
const comments2 = since ? await listLinearCommentsSince({
|
|
22716
|
+
since,
|
|
22717
|
+
assignee,
|
|
22718
|
+
query,
|
|
22719
|
+
issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
|
|
22720
|
+
limit: opts?.limit
|
|
22721
|
+
}) : void 0;
|
|
22689
22722
|
return {
|
|
22690
22723
|
source,
|
|
22691
22724
|
preferredSource,
|
|
@@ -24040,20 +24073,19 @@ function registerLinearTools(server) {
|
|
|
24040
24073
|
try {
|
|
24041
24074
|
const page = clampMcpIssueLimit(limit);
|
|
24042
24075
|
const since = updatedSince ? parseIssueSince(updatedSince) : void 0;
|
|
24043
|
-
const
|
|
24044
|
-
|
|
24045
|
-
|
|
24046
|
-
|
|
24047
|
-
|
|
24048
|
-
|
|
24049
|
-
|
|
24050
|
-
since
|
|
24051
|
-
|
|
24052
|
-
|
|
24053
|
-
|
|
24054
|
-
|
|
24055
|
-
|
|
24056
|
-
]);
|
|
24076
|
+
const listed = await listLinearIssuesFiltered({
|
|
24077
|
+
query,
|
|
24078
|
+
assignee,
|
|
24079
|
+
limit: page + 1,
|
|
24080
|
+
updatedSince: since
|
|
24081
|
+
});
|
|
24082
|
+
const comments = since ? await listLinearCommentsSince({
|
|
24083
|
+
since,
|
|
24084
|
+
assignee,
|
|
24085
|
+
query,
|
|
24086
|
+
issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
|
|
24087
|
+
limit: page + 1
|
|
24088
|
+
}) : void 0;
|
|
24057
24089
|
return mcpJson(
|
|
24058
24090
|
formatListedIssuesForMcp(
|
|
24059
24091
|
{
|
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
syncBoardPins,
|
|
38
38
|
updateSchedule,
|
|
39
39
|
waitForDetachedJob
|
|
40
|
-
} from "../chunk-
|
|
40
|
+
} from "../chunk-IQC35TVQ.js";
|
|
41
41
|
import {
|
|
42
42
|
applySystemCaEnv,
|
|
43
43
|
isCursorApiKeyConfigured,
|
|
@@ -262,10 +262,32 @@ function issueNumberFromApiUrl(url) {
|
|
|
262
262
|
const number = Number(match[1]);
|
|
263
263
|
return Number.isFinite(number) && number > 0 ? number : null;
|
|
264
264
|
}
|
|
265
|
+
var GITHUB_ISSUE_COMMENTS_PAGE_SIZE = 100;
|
|
266
|
+
var GITHUB_ISSUE_COMMENTS_MAX_PAGES = 5;
|
|
267
|
+
function parseGitHubIssueCommentNode(item, allowed) {
|
|
268
|
+
const rec = item && typeof item === "object" ? item : null;
|
|
269
|
+
if (!rec) return null;
|
|
270
|
+
const issueUrl = typeof rec.issue_url === "string" ? rec.issue_url : "";
|
|
271
|
+
const number = issueNumberFromApiUrl(issueUrl);
|
|
272
|
+
if (number == null) return null;
|
|
273
|
+
if (allowed && !allowed.has(number)) return null;
|
|
274
|
+
const body = previewIssueCommentBody(typeof rec.body === "string" ? rec.body : "");
|
|
275
|
+
const user = rec.user && typeof rec.user === "object" ? String(rec.user.login ?? "").trim() : "";
|
|
276
|
+
const createdAt = typeof rec.created_at === "string" ? rec.created_at : typeof rec.createdAt === "string" ? rec.createdAt : void 0;
|
|
277
|
+
const url = typeof rec.html_url === "string" ? rec.html_url : void 0;
|
|
278
|
+
return {
|
|
279
|
+
identifier: `#${number}`,
|
|
280
|
+
...user ? { author: user } : {},
|
|
281
|
+
...createdAt ? { createdAt } : {},
|
|
282
|
+
body,
|
|
283
|
+
...url ? { url } : {}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
265
286
|
async function listGitHubIssueCommentsSince(opts) {
|
|
266
287
|
const limit = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
267
288
|
const { cwd, slug } = await resolveGitHubIssueRepo(opts.repoPath);
|
|
268
289
|
if (!slug) return [];
|
|
290
|
+
const restrictToIssues = opts.issueIdentifiers != null;
|
|
269
291
|
const allowed = new Set(
|
|
270
292
|
(opts.issueIdentifiers ?? []).map((id) => {
|
|
271
293
|
try {
|
|
@@ -275,36 +297,27 @@ async function listGitHubIssueCommentsSince(opts) {
|
|
|
275
297
|
}
|
|
276
298
|
}).filter((n) => n != null)
|
|
277
299
|
);
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
if (result.exitCode !== 0 || !result.stdout.trim()) return [];
|
|
281
|
-
let parsed;
|
|
282
|
-
try {
|
|
283
|
-
parsed = JSON.parse(result.stdout);
|
|
284
|
-
} catch {
|
|
285
|
-
return [];
|
|
286
|
-
}
|
|
287
|
-
if (!Array.isArray(parsed)) return [];
|
|
300
|
+
if (restrictToIssues && allowed.size === 0) return [];
|
|
301
|
+
const pageSize = Math.min(GITHUB_ISSUE_COMMENTS_PAGE_SIZE, Math.max(1, limit));
|
|
288
302
|
const out = [];
|
|
289
|
-
for (
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (out.length >= limit) break;
|
|
303
|
+
for (let page = 1; page <= GITHUB_ISSUE_COMMENTS_MAX_PAGES; page++) {
|
|
304
|
+
const path = `repos/${slug}/issues/comments?since=${encodeURIComponent(opts.since)}&sort=created&direction=desc&per_page=${pageSize}&page=${page}`;
|
|
305
|
+
const result = await gh(["api", path], cwd, { reject: false });
|
|
306
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) break;
|
|
307
|
+
let parsed;
|
|
308
|
+
try {
|
|
309
|
+
parsed = JSON.parse(result.stdout);
|
|
310
|
+
} catch {
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
if (!Array.isArray(parsed) || parsed.length === 0) break;
|
|
314
|
+
for (const item of parsed) {
|
|
315
|
+
const comment = parseGitHubIssueCommentNode(item, restrictToIssues ? allowed : null);
|
|
316
|
+
if (!comment) continue;
|
|
317
|
+
out.push(comment);
|
|
318
|
+
if (out.length >= limit) return out;
|
|
319
|
+
}
|
|
320
|
+
if (parsed.length < pageSize) break;
|
|
308
321
|
}
|
|
309
322
|
return out;
|
|
310
323
|
}
|
|
@@ -909,12 +922,27 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
909
922
|
function assigneeFilterKey(assignee) {
|
|
910
923
|
return (assignee ?? "").trim();
|
|
911
924
|
}
|
|
925
|
+
function applyLinearIssueQueryFilter(filter, query) {
|
|
926
|
+
const term = query?.trim();
|
|
927
|
+
if (!term) return;
|
|
928
|
+
const or = [
|
|
929
|
+
{ title: { containsIgnoreCase: term } },
|
|
930
|
+
{ description: { containsIgnoreCase: term } }
|
|
931
|
+
];
|
|
932
|
+
const identifierMatch = term.match(/^[A-Za-z][\w]*-(\d+)$/);
|
|
933
|
+
const asNumber = identifierMatch ? Number(identifierMatch[1]) : /^\d+$/.test(term) ? Number(term) : NaN;
|
|
934
|
+
if (Number.isFinite(asNumber) && asNumber > 0) {
|
|
935
|
+
or.push({ number: { eq: asNumber } });
|
|
936
|
+
}
|
|
937
|
+
filter.or = or;
|
|
938
|
+
}
|
|
912
939
|
function buildLinearIssueFilter(input) {
|
|
913
940
|
const filter = {
|
|
914
941
|
state: { type: { nin: ["completed", "canceled"] } }
|
|
915
942
|
};
|
|
916
943
|
const since = input?.updatedSince?.trim();
|
|
917
944
|
if (since) filter.updatedAt = { gte: since };
|
|
945
|
+
applyLinearIssueQueryFilter(filter, input?.query);
|
|
918
946
|
const raw = assigneeFilterKey(input?.assignee) || "me";
|
|
919
947
|
const key = raw.toLowerCase();
|
|
920
948
|
if (key === "all" || key === "*") return filter;
|
|
@@ -980,6 +1008,11 @@ async function listLinearIssuesFiltered(opts) {
|
|
|
980
1008
|
async function listLinearCommentsSince(opts) {
|
|
981
1009
|
const first = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
982
1010
|
const query = opts.query?.trim() ?? "";
|
|
1011
|
+
const restrictToIssues = opts.issueIdentifiers != null;
|
|
1012
|
+
const allowed = new Set(
|
|
1013
|
+
(opts.issueIdentifiers ?? []).map((id) => id.trim()).filter(Boolean)
|
|
1014
|
+
);
|
|
1015
|
+
if (restrictToIssues && allowed.size === 0) return [];
|
|
983
1016
|
const assignee = assigneeFilterKey(opts.assignee) || (query ? "all" : "me");
|
|
984
1017
|
const json = await linearGraphql(
|
|
985
1018
|
COMMENTS_SINCE_QUERY,
|
|
@@ -987,7 +1020,7 @@ async function listLinearCommentsSince(opts) {
|
|
|
987
1020
|
first,
|
|
988
1021
|
filter: {
|
|
989
1022
|
createdAt: { gte: opts.since },
|
|
990
|
-
issue: buildLinearIssueFilter({ assignee })
|
|
1023
|
+
issue: buildLinearIssueFilter({ assignee, query: query || void 0 })
|
|
991
1024
|
}
|
|
992
1025
|
},
|
|
993
1026
|
opts
|
|
@@ -995,6 +1028,7 @@ async function listLinearCommentsSince(opts) {
|
|
|
995
1028
|
const out = [];
|
|
996
1029
|
for (const node of json.comments?.nodes ?? []) {
|
|
997
1030
|
const identifier = String(node.issue?.identifier ?? "").trim();
|
|
1031
|
+
if (restrictToIssues && !allowed.has(identifier)) continue;
|
|
998
1032
|
const body = previewIssueCommentBody(String(node.body ?? ""));
|
|
999
1033
|
if (!identifier && !body) continue;
|
|
1000
1034
|
out.push({
|
|
@@ -1220,20 +1254,19 @@ async function listIssues(repoPath, opts) {
|
|
|
1220
1254
|
const assignee = opts?.assignee?.trim() || void 0;
|
|
1221
1255
|
const since = opts?.updatedSince ? parseIssueSince(opts.updatedSince) : void 0;
|
|
1222
1256
|
if (source === "linear") {
|
|
1223
|
-
const
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
since
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
]);
|
|
1257
|
+
const listed = await listLinearIssuesFiltered({
|
|
1258
|
+
assignee,
|
|
1259
|
+
query,
|
|
1260
|
+
limit: opts?.limit,
|
|
1261
|
+
updatedSince: since
|
|
1262
|
+
});
|
|
1263
|
+
const comments2 = since ? await listLinearCommentsSince({
|
|
1264
|
+
since,
|
|
1265
|
+
assignee,
|
|
1266
|
+
query,
|
|
1267
|
+
issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
|
|
1268
|
+
limit: opts?.limit
|
|
1269
|
+
}) : void 0;
|
|
1237
1270
|
return {
|
|
1238
1271
|
source,
|
|
1239
1272
|
preferredSource,
|
|
@@ -2563,20 +2596,19 @@ function registerLinearTools(server) {
|
|
|
2563
2596
|
try {
|
|
2564
2597
|
const page = clampMcpIssueLimit(limit);
|
|
2565
2598
|
const since = updatedSince ? parseIssueSince(updatedSince) : void 0;
|
|
2566
|
-
const
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
since
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
]);
|
|
2599
|
+
const listed = await listLinearIssuesFiltered({
|
|
2600
|
+
query,
|
|
2601
|
+
assignee,
|
|
2602
|
+
limit: page + 1,
|
|
2603
|
+
updatedSince: since
|
|
2604
|
+
});
|
|
2605
|
+
const comments = since ? await listLinearCommentsSince({
|
|
2606
|
+
since,
|
|
2607
|
+
assignee,
|
|
2608
|
+
query,
|
|
2609
|
+
issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
|
|
2610
|
+
limit: page + 1
|
|
2611
|
+
}) : void 0;
|
|
2580
2612
|
return mcpJson(
|
|
2581
2613
|
formatListedIssuesForMcp(
|
|
2582
2614
|
{
|