@sideboard-ai/core 0.1.190 → 0.1.191
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/{abletime-J3EGHIAV.js → abletime-53T7G667.js} +3 -1
- package/dist/{abletime-CAFQ4GEG.js → abletime-JFMBCSIQ.js} +3 -1
- package/dist/{chunk-TEJU6UJY.js → chunk-4M2AK3O5.js} +126 -20
- package/dist/{chunk-Z6TW3A5P.js → chunk-5RRAKGPK.js} +6 -6
- package/dist/{chunk-IAOXW6RI.js → chunk-GPSIZGEZ.js} +6 -6
- package/dist/{chunk-KEVY4GEK.js → chunk-UBQIELFS.js} +123 -20
- package/dist/index.cjs +507 -46
- package/dist/index.d.cts +88 -3
- package/dist/index.d.ts +88 -3
- package/dist/index.js +376 -24
- package/dist/mcp/run-stdio.cjs +487 -46
- package/dist/mcp/run-stdio.js +362 -24
- package/dist/{orchestrator-BHJELEFM.js → orchestrator-2X6LYBQI.js} +1 -1
- package/dist/{orchestrator-HWNGCDGW.js → orchestrator-F3CXDBZB.js} +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -16888,13 +16888,17 @@ var init_home_board = __esm({
|
|
|
16888
16888
|
});
|
|
16889
16889
|
|
|
16890
16890
|
// src/integrations/abletime-mcp.ts
|
|
16891
|
+
function abletimeToolUsesPm(name) {
|
|
16892
|
+
return ABLETIME_PM_TOOL_NAMES.includes(name);
|
|
16893
|
+
}
|
|
16891
16894
|
function normalizeAbleTimeHost(raw) {
|
|
16892
16895
|
const trimmed = raw?.trim() || DEFAULT_ABLETIME_HOST;
|
|
16893
16896
|
const withScheme = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
16894
16897
|
return withScheme.replace(/\/+$/, "");
|
|
16895
16898
|
}
|
|
16896
|
-
function abletimeMcpUrl(host) {
|
|
16897
|
-
|
|
16899
|
+
function abletimeMcpUrl(host, opts) {
|
|
16900
|
+
const path2 = opts?.pm ? ABLETIME_MCP_PM_PATH : ABLETIME_MCP_PATH;
|
|
16901
|
+
return `${normalizeAbleTimeHost(host)}${path2}`;
|
|
16898
16902
|
}
|
|
16899
16903
|
function rewriteAbleTimeError(message) {
|
|
16900
16904
|
if (/INTEGRATION_AGENT_ACCESS_DISABLED|agent access/i.test(message)) {
|
|
@@ -16972,7 +16976,8 @@ async function abletimeMcpRequest(method, params, opts) {
|
|
|
16972
16976
|
throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
|
|
16973
16977
|
}
|
|
16974
16978
|
const url = abletimeMcpUrl(
|
|
16975
|
-
opts?.host ?? (opts?.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost())
|
|
16979
|
+
opts?.host ?? (opts?.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost()),
|
|
16980
|
+
{ pm: opts?.pm }
|
|
16976
16981
|
);
|
|
16977
16982
|
const res = await httpFetch(url, {
|
|
16978
16983
|
method: "POST",
|
|
@@ -17036,7 +17041,8 @@ async function callAbleTimeTool(name, args = {}, opts) {
|
|
|
17036
17041
|
if (!opts?.token && !isAbleTimeConnected()) {
|
|
17037
17042
|
throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
|
|
17038
17043
|
}
|
|
17039
|
-
const
|
|
17044
|
+
const requestOpts = { ...opts, pm: opts?.pm ?? abletimeToolUsesPm(name) };
|
|
17045
|
+
const call = () => abletimeMcpRequest("tools/call", { name, arguments: args }, requestOpts).then((result) => {
|
|
17040
17046
|
return unwrapToolResult(result);
|
|
17041
17047
|
});
|
|
17042
17048
|
try {
|
|
@@ -17044,13 +17050,13 @@ async function callAbleTimeTool(name, args = {}, opts) {
|
|
|
17044
17050
|
} catch (err) {
|
|
17045
17051
|
const message = err instanceof Error ? err.message : String(err);
|
|
17046
17052
|
if (/initialize|session|not initialized|-32000/i.test(message)) {
|
|
17047
|
-
await initializeIfNeeded(
|
|
17053
|
+
await initializeIfNeeded(requestOpts);
|
|
17048
17054
|
return await call();
|
|
17049
17055
|
}
|
|
17050
17056
|
throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
|
|
17051
17057
|
}
|
|
17052
17058
|
}
|
|
17053
|
-
var DEFAULT_ABLETIME_HOST, ABLETIME_MCP_PATH, MCP_PROTOCOL_VERSION;
|
|
17059
|
+
var DEFAULT_ABLETIME_HOST, ABLETIME_MCP_PATH, ABLETIME_MCP_PM_PATH, MCP_PROTOCOL_VERSION, ABLETIME_PM_TOOL_NAMES;
|
|
17054
17060
|
var init_abletime_mcp = __esm({
|
|
17055
17061
|
"src/integrations/abletime-mcp.ts"() {
|
|
17056
17062
|
"use strict";
|
|
@@ -17058,13 +17064,27 @@ var init_abletime_mcp = __esm({
|
|
|
17058
17064
|
init_app_settings();
|
|
17059
17065
|
DEFAULT_ABLETIME_HOST = "https://track.abletime.com";
|
|
17060
17066
|
ABLETIME_MCP_PATH = "/api/public/v2/mcp";
|
|
17067
|
+
ABLETIME_MCP_PM_PATH = "/api/public/v2/mcp/pm";
|
|
17061
17068
|
MCP_PROTOCOL_VERSION = "2025-03-26";
|
|
17069
|
+
ABLETIME_PM_TOOL_NAMES = [
|
|
17070
|
+
"set_task_assignee",
|
|
17071
|
+
"set_task_priority",
|
|
17072
|
+
"set_task_stage",
|
|
17073
|
+
"set_task_dependency",
|
|
17074
|
+
"set_task_blocked",
|
|
17075
|
+
"set_task_locked",
|
|
17076
|
+
"set_task_schedule",
|
|
17077
|
+
"clear_task_schedule",
|
|
17078
|
+
"archive_task",
|
|
17079
|
+
"delete_task"
|
|
17080
|
+
];
|
|
17062
17081
|
}
|
|
17063
17082
|
});
|
|
17064
17083
|
|
|
17065
17084
|
// src/integrations/abletime.ts
|
|
17066
17085
|
var abletime_exports = {};
|
|
17067
17086
|
__export(abletime_exports, {
|
|
17087
|
+
ableTimeDependencyTarget: () => ableTimeDependencyTarget,
|
|
17068
17088
|
commentAbleTimeTask: () => commentAbleTimeTask,
|
|
17069
17089
|
createAbleTimeTask: () => createAbleTimeTask,
|
|
17070
17090
|
ensureAbleTimeTask: () => ensureAbleTimeTask,
|
|
@@ -17321,39 +17341,122 @@ async function commentAbleTimeTask(input, opts) {
|
|
|
17321
17341
|
body: rec ? firstString(rec, ["body", "text", "comment"]) || body : body
|
|
17322
17342
|
};
|
|
17323
17343
|
}
|
|
17344
|
+
function isAbleTimeNoneToken(value) {
|
|
17345
|
+
if (value == null) return true;
|
|
17346
|
+
const s = value.trim();
|
|
17347
|
+
return !s || /^(none|clear|unschedule|null)$/i.test(s);
|
|
17348
|
+
}
|
|
17349
|
+
function taskIdArgs(id) {
|
|
17350
|
+
return { id, task_id: id, task: id };
|
|
17351
|
+
}
|
|
17352
|
+
function ableTimeDependencyTarget(thisId, type, relatedId) {
|
|
17353
|
+
const s = type.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
17354
|
+
if (s === "none" || s === "clear" || s === "null") {
|
|
17355
|
+
return { taskId: thisId, dependsOn: null };
|
|
17356
|
+
}
|
|
17357
|
+
if (s === "blockedby" || s === "blocked" || s === "dependson" || s === "waiton") {
|
|
17358
|
+
return { taskId: thisId, dependsOn: relatedId };
|
|
17359
|
+
}
|
|
17360
|
+
if (s === "blocks" || s === "blocking") {
|
|
17361
|
+
return { taskId: relatedId, dependsOn: thisId };
|
|
17362
|
+
}
|
|
17363
|
+
throw new Error(
|
|
17364
|
+
`AbleTime relation type must be blocks or blockedBy (got ${type}). AbleTime models one wait-on dependency.`
|
|
17365
|
+
);
|
|
17366
|
+
}
|
|
17367
|
+
async function setAbleTimeTaskDependency(taskId, dependsOn, opts) {
|
|
17368
|
+
const args = {
|
|
17369
|
+
...taskIdArgs(taskId)
|
|
17370
|
+
};
|
|
17371
|
+
if (dependsOn) {
|
|
17372
|
+
args.depends_on = dependsOn;
|
|
17373
|
+
args.depends_on_id = dependsOn;
|
|
17374
|
+
args.dependency = dependsOn;
|
|
17375
|
+
args.dependency_id = dependsOn;
|
|
17376
|
+
args.blocked_by = dependsOn;
|
|
17377
|
+
args.wait_on = dependsOn;
|
|
17378
|
+
args.related_task_id = dependsOn;
|
|
17379
|
+
} else {
|
|
17380
|
+
args.depends_on = null;
|
|
17381
|
+
args.dependency = null;
|
|
17382
|
+
args.blocked_by = null;
|
|
17383
|
+
args.clear = true;
|
|
17384
|
+
}
|
|
17385
|
+
await callAbleTimeTool("set_task_dependency", args, opts);
|
|
17386
|
+
}
|
|
17324
17387
|
async function updateAbleTimeTask(input, opts) {
|
|
17325
17388
|
const id = input.id.trim();
|
|
17326
17389
|
if (!id) throw new Error("AbleTime task id is required");
|
|
17327
17390
|
const title = input.title?.trim();
|
|
17328
17391
|
const description = input.description;
|
|
17329
17392
|
const state = input.state?.trim();
|
|
17330
|
-
|
|
17331
|
-
|
|
17393
|
+
const addRelations = input.relations?.length ? input.relations : void 0;
|
|
17394
|
+
const removeRelations = input.removeRelations?.length ? input.removeRelations : void 0;
|
|
17395
|
+
const updateArgs = { ...taskIdArgs(id) };
|
|
17396
|
+
if (title) updateArgs.title = title;
|
|
17397
|
+
if (description !== void 0) updateArgs.description = description;
|
|
17398
|
+
if (input.labels !== void 0) {
|
|
17399
|
+
updateArgs.tags = input.labels;
|
|
17400
|
+
updateArgs.labels = input.labels;
|
|
17401
|
+
updateArgs.tag_names = input.labels;
|
|
17402
|
+
}
|
|
17403
|
+
if (input.project !== void 0) {
|
|
17404
|
+
const projectName = (input.project ?? "").trim();
|
|
17405
|
+
if (isAbleTimeNoneToken(projectName)) {
|
|
17406
|
+
throw new Error("AbleTime tasks stay in a project \u2014 pass a project name or id");
|
|
17407
|
+
}
|
|
17408
|
+
const project = await resolveAbleTimeProject(projectName, opts);
|
|
17409
|
+
updateArgs.project = project.id;
|
|
17410
|
+
updateArgs.project_id = project.id;
|
|
17411
|
+
}
|
|
17412
|
+
if (input.parent !== void 0) {
|
|
17413
|
+
if (isAbleTimeNoneToken(input.parent)) {
|
|
17414
|
+
updateArgs.parent = null;
|
|
17415
|
+
updateArgs.parent_id = null;
|
|
17416
|
+
updateArgs.related_task_id = null;
|
|
17417
|
+
} else {
|
|
17418
|
+
const parent = (input.parent ?? "").trim();
|
|
17419
|
+
updateArgs.parent = parent;
|
|
17420
|
+
updateArgs.parent_id = parent;
|
|
17421
|
+
updateArgs.related_task_id = parent;
|
|
17422
|
+
}
|
|
17423
|
+
}
|
|
17424
|
+
const hasUpdateFields = Boolean(title) || description !== void 0 || input.labels !== void 0 || input.project !== void 0 || input.parent !== void 0;
|
|
17425
|
+
if (!hasUpdateFields && !state && !addRelations && !removeRelations) {
|
|
17426
|
+
throw new Error(
|
|
17427
|
+
"abletime_update_task needs at least one of title, description, state, labels, project, parent, relations, removeRelations"
|
|
17428
|
+
);
|
|
17332
17429
|
}
|
|
17333
17430
|
if (state) {
|
|
17334
17431
|
await callAbleTimeTool(
|
|
17335
17432
|
"set_task_state",
|
|
17336
|
-
{ id,
|
|
17433
|
+
{ ...taskIdArgs(id), state },
|
|
17337
17434
|
opts
|
|
17338
17435
|
).catch(async () => {
|
|
17339
17436
|
await callAbleTimeTool(
|
|
17340
17437
|
"update_task",
|
|
17341
|
-
{
|
|
17438
|
+
{ ...updateArgs, state },
|
|
17342
17439
|
opts
|
|
17343
17440
|
);
|
|
17344
17441
|
});
|
|
17345
17442
|
}
|
|
17346
|
-
if (
|
|
17347
|
-
await callAbleTimeTool(
|
|
17348
|
-
|
|
17349
|
-
|
|
17350
|
-
|
|
17351
|
-
|
|
17352
|
-
|
|
17353
|
-
|
|
17354
|
-
|
|
17355
|
-
|
|
17356
|
-
);
|
|
17443
|
+
if (hasUpdateFields) {
|
|
17444
|
+
await callAbleTimeTool("update_task", updateArgs, opts);
|
|
17445
|
+
}
|
|
17446
|
+
for (const rel of removeRelations ?? []) {
|
|
17447
|
+
const related = rel.issue.trim() || id;
|
|
17448
|
+
const target = ableTimeDependencyTarget(id, rel.type, related);
|
|
17449
|
+
await setAbleTimeTaskDependency(target.taskId, null, opts);
|
|
17450
|
+
}
|
|
17451
|
+
for (const rel of addRelations ?? []) {
|
|
17452
|
+
const related = rel.issue.trim();
|
|
17453
|
+
if (!related) throw new Error("AbleTime relations.issue is required (id or CRM-232)");
|
|
17454
|
+
const target = ableTimeDependencyTarget(id, rel.type, related);
|
|
17455
|
+
if (!target.dependsOn) {
|
|
17456
|
+
await setAbleTimeTaskDependency(target.taskId, null, opts);
|
|
17457
|
+
continue;
|
|
17458
|
+
}
|
|
17459
|
+
await setAbleTimeTaskDependency(target.taskId, target.dependsOn, opts);
|
|
17357
17460
|
}
|
|
17358
17461
|
return getAbleTimeTask(id, opts);
|
|
17359
17462
|
}
|
|
@@ -20031,17 +20134,17 @@ function formatIssueToolsDirective(opts) {
|
|
|
20031
20134
|
];
|
|
20032
20135
|
if (opts.linear) {
|
|
20033
20136
|
lines.push(
|
|
20034
|
-
"- Linear: `linear_search_issues` (assignee=me; pass `updatedSince` for \u201Cany updates since yesterday?\u201D \u2014 new tickets, edits, and comments), `linear_get_issue` (comments; pass `include=full` if crush.truncated), `linear_comment`, `linear_update_issue` (state, cycle), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
20137
|
+
"- Linear: `linear_search_issues` (assignee=me; pass `updatedSince` for \u201Cany updates since yesterday?\u201D \u2014 new tickets, edits, and comments), `linear_get_issue` (comments; pass `include=full` if crush.truncated), `linear_comment`, `linear_update_issue` (state, cycle, labels, project, parent, relations), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
20035
20138
|
);
|
|
20036
20139
|
}
|
|
20037
20140
|
if (github) {
|
|
20038
20141
|
lines.push(
|
|
20039
|
-
"- GitHub: `github_search_issues` (assignee=me; `updatedSince` for inbox updates + comments), `github_get_issue` (comments; pass `include=full` if crush.truncated), `github_comment`, `github_update_issue` (state open|closed), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
20142
|
+
"- GitHub: `github_search_issues` (assignee=me; `updatedSince` for inbox updates + comments), `github_get_issue` (comments; pass `include=full` if crush.truncated), `github_comment`, `github_update_issue` (state open|closed, labels, project, parent, relations), `github_create_issue` (pass `parent` for spin-offs). Uses Account `gh`."
|
|
20040
20143
|
);
|
|
20041
20144
|
}
|
|
20042
20145
|
if (opts.abletime) {
|
|
20043
20146
|
lines.push(
|
|
20044
|
-
"- AbleTime: `abletime_list_tasks` / `abletime_search_tasks` (`updatedSince` for inbox updates + comments), `abletime_get_task` (comments; pass `include=full` if crush.truncated), `abletime_comment`, `abletime_update_task` (state), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
20147
|
+
"- AbleTime: `abletime_list_tasks` / `abletime_search_tasks` (`updatedSince` for inbox updates + comments), `abletime_get_task` (comments; pass `include=full` if crush.truncated), `abletime_comment`, `abletime_update_task` (state, labels, project, parent, relations), `abletime_create_task` (pass `parent` for spin-offs; `abletime_list_projects` if needed)."
|
|
20045
20148
|
);
|
|
20046
20149
|
}
|
|
20047
20150
|
lines.push(
|
|
@@ -23131,6 +23234,8 @@ var init_schedule_runner = __esm({
|
|
|
23131
23234
|
var index_exports = {};
|
|
23132
23235
|
__export(index_exports, {
|
|
23133
23236
|
ABLETIME_MCP_PATH: () => ABLETIME_MCP_PATH,
|
|
23237
|
+
ABLETIME_MCP_PM_PATH: () => ABLETIME_MCP_PM_PATH,
|
|
23238
|
+
ABLETIME_PM_TOOL_NAMES: () => ABLETIME_PM_TOOL_NAMES,
|
|
23134
23239
|
AGENT_GIT_ACTIONS: () => AGENT_GIT_ACTIONS,
|
|
23135
23240
|
AGENT_RUNNER_MAX_OLD_SPACE_MB: () => AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
23136
23241
|
ATTACHMENTS_DIR: () => ATTACHMENTS_DIR,
|
|
@@ -23220,8 +23325,10 @@ __export(index_exports, {
|
|
|
23220
23325
|
WORKTREE_GITHUB_MCP_TOOLS: () => WORKTREE_GITHUB_MCP_TOOLS,
|
|
23221
23326
|
WORKTREE_LINEAR_MCP_TOOLS: () => WORKTREE_LINEAR_MCP_TOOLS,
|
|
23222
23327
|
WORKTREE_MCP_TOOLS: () => WORKTREE_MCP_TOOLS,
|
|
23328
|
+
ableTimeDependencyTarget: () => ableTimeDependencyTarget,
|
|
23223
23329
|
abletimeMcpRequest: () => abletimeMcpRequest,
|
|
23224
23330
|
abletimeMcpUrl: () => abletimeMcpUrl,
|
|
23331
|
+
abletimeToolUsesPm: () => abletimeToolUsesPm,
|
|
23225
23332
|
ackSlackInboundSeen: () => ackSlackInboundSeen,
|
|
23226
23333
|
addBoardPin: () => addBoardPin,
|
|
23227
23334
|
addPrStackLayer: () => addPrStackLayer,
|
|
@@ -23494,6 +23601,7 @@ __export(index_exports, {
|
|
|
23494
23601
|
git: () => git,
|
|
23495
23602
|
githubAgentGitEnv: () => githubAgentGitEnv,
|
|
23496
23603
|
githubHttpsInsteadOfEntries: () => githubHttpsInsteadOfEntries,
|
|
23604
|
+
githubRelationEditFlag: () => githubRelationEditFlag,
|
|
23497
23605
|
globalAgentCwd: () => globalAgentCwd,
|
|
23498
23606
|
groupHomeBoardWorktrees: () => groupHomeBoardWorktrees,
|
|
23499
23607
|
handleSlackInbound: () => handleSlackInbound,
|
|
@@ -23543,6 +23651,7 @@ __export(index_exports, {
|
|
|
23543
23651
|
isInternalAgentStatusText: () => isInternalAgentStatusText,
|
|
23544
23652
|
isIssueSourceConnected: () => isIssueSourceConnected,
|
|
23545
23653
|
isLinearConnected: () => isLinearConnected,
|
|
23654
|
+
isLinearNoneToken: () => isLinearNoneToken,
|
|
23546
23655
|
isLinearOAuthCancelled: () => isLinearOAuthCancelled,
|
|
23547
23656
|
isOptionalServiceId: () => isOptionalServiceId,
|
|
23548
23657
|
isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
|
|
@@ -23580,6 +23689,7 @@ __export(index_exports, {
|
|
|
23580
23689
|
linearGraphql: () => linearGraphql,
|
|
23581
23690
|
linearOAuthAuthorizeUrl: () => linearOAuthAuthorizeUrl,
|
|
23582
23691
|
linearOAuthCredentials: () => linearOAuthCredentials,
|
|
23692
|
+
linearRelationCreateInput: () => linearRelationCreateInput,
|
|
23583
23693
|
linearTicketIdFromThread: () => linearTicketIdFromThread,
|
|
23584
23694
|
listAbleTimeAssignedIssues: () => listAbleTimeAssignedIssues,
|
|
23585
23695
|
listAbleTimeProjects: () => listAbleTimeProjects,
|
|
@@ -23648,6 +23758,7 @@ __export(index_exports, {
|
|
|
23648
23758
|
nonInteractiveGitProcessEnv: () => nonInteractiveGitProcessEnv,
|
|
23649
23759
|
normalizeAbleTimeHost: () => normalizeAbleTimeHost,
|
|
23650
23760
|
normalizeCodeSelection: () => normalizeCodeSelection,
|
|
23761
|
+
normalizeLinearRelationType: () => normalizeLinearRelationType,
|
|
23651
23762
|
normalizeParseResult: () => normalizeParseResult,
|
|
23652
23763
|
normalizeServiceOrigin: () => normalizeServiceOrigin,
|
|
23653
23764
|
normalizeThinkingEffort: () => normalizeThinkingEffort,
|
|
@@ -23741,6 +23852,8 @@ __export(index_exports, {
|
|
|
23741
23852
|
resolveGithubAgentToken: () => resolveGithubAgentToken,
|
|
23742
23853
|
resolveGithubRepoSlug: () => resolveGithubRepoSlug,
|
|
23743
23854
|
resolveLinearCycle: () => resolveLinearCycle,
|
|
23855
|
+
resolveLinearLabel: () => resolveLinearLabel,
|
|
23856
|
+
resolveLinearProject: () => resolveLinearProject,
|
|
23744
23857
|
resolveLinearState: () => resolveLinearState,
|
|
23745
23858
|
resolveLinearTeam: () => resolveLinearTeam,
|
|
23746
23859
|
resolveLoginCommand: () => resolveLoginCommand,
|
|
@@ -24358,12 +24471,14 @@ var ISSUE_FIELDS = `
|
|
|
24358
24471
|
children(first: 25) { nodes { ${ISSUE_REF_FIELDS} } }
|
|
24359
24472
|
relations(first: 25) {
|
|
24360
24473
|
nodes {
|
|
24474
|
+
id
|
|
24361
24475
|
type
|
|
24362
24476
|
relatedIssue { ${ISSUE_REF_FIELDS} }
|
|
24363
24477
|
}
|
|
24364
24478
|
}
|
|
24365
24479
|
inverseRelations(first: 25) {
|
|
24366
24480
|
nodes {
|
|
24481
|
+
id
|
|
24367
24482
|
type
|
|
24368
24483
|
issue { ${ISSUE_REF_FIELDS} }
|
|
24369
24484
|
}
|
|
@@ -24483,6 +24598,35 @@ mutation SideboardCommentCreate($input: CommentCreateInput!) {
|
|
|
24483
24598
|
}
|
|
24484
24599
|
}
|
|
24485
24600
|
`;
|
|
24601
|
+
var LABELS_QUERY = `
|
|
24602
|
+
query SideboardLabels($first: Int!) {
|
|
24603
|
+
issueLabels(first: $first) {
|
|
24604
|
+
nodes { id name }
|
|
24605
|
+
}
|
|
24606
|
+
}
|
|
24607
|
+
`;
|
|
24608
|
+
var PROJECTS_QUERY = `
|
|
24609
|
+
query SideboardProjects($first: Int!, $filter: ProjectFilter) {
|
|
24610
|
+
projects(first: $first, filter: $filter) {
|
|
24611
|
+
nodes { id name slugId }
|
|
24612
|
+
}
|
|
24613
|
+
}
|
|
24614
|
+
`;
|
|
24615
|
+
var ISSUE_RELATION_CREATE = `
|
|
24616
|
+
mutation SideboardIssueRelationCreate($input: IssueRelationCreateInput!) {
|
|
24617
|
+
issueRelationCreate(input: $input) {
|
|
24618
|
+
success
|
|
24619
|
+
issueRelation { id type }
|
|
24620
|
+
}
|
|
24621
|
+
}
|
|
24622
|
+
`;
|
|
24623
|
+
var ISSUE_RELATION_DELETE = `
|
|
24624
|
+
mutation SideboardIssueRelationDelete($id: String!) {
|
|
24625
|
+
issueRelationDelete(id: $id) {
|
|
24626
|
+
success
|
|
24627
|
+
}
|
|
24628
|
+
}
|
|
24629
|
+
`;
|
|
24486
24630
|
function rewriteLinearError(message) {
|
|
24487
24631
|
if (/scope|permission|not (authorized|allowed)|unauthorized|insufficient/i.test(message)) {
|
|
24488
24632
|
return `${message} \u2014 Disconnect and Connect Linear in Account settings to grant write access.`;
|
|
@@ -24602,12 +24746,18 @@ function mapRelations(node) {
|
|
|
24602
24746
|
for (const rel of node.relations?.nodes ?? []) {
|
|
24603
24747
|
const issue = mapIssueRef(rel.relatedIssue);
|
|
24604
24748
|
if (!issue) continue;
|
|
24605
|
-
|
|
24749
|
+
const id = rel.id?.trim();
|
|
24750
|
+
out.push({ type: String(rel.type ?? "related"), issue, ...id ? { id } : {} });
|
|
24606
24751
|
}
|
|
24607
24752
|
for (const rel of node.inverseRelations?.nodes ?? []) {
|
|
24608
24753
|
const issue = mapIssueRef(rel.issue);
|
|
24609
24754
|
if (!issue) continue;
|
|
24610
|
-
|
|
24755
|
+
const id = rel.id?.trim();
|
|
24756
|
+
out.push({
|
|
24757
|
+
type: flipLinearRelationType(String(rel.type ?? "related")),
|
|
24758
|
+
issue,
|
|
24759
|
+
...id ? { id } : {}
|
|
24760
|
+
});
|
|
24611
24761
|
}
|
|
24612
24762
|
return out;
|
|
24613
24763
|
}
|
|
@@ -24768,6 +24918,148 @@ function resolveLinearCycle(team, cycle) {
|
|
|
24768
24918
|
}
|
|
24769
24919
|
return found.id;
|
|
24770
24920
|
}
|
|
24921
|
+
var LINEAR_NONE_RE = /^(none|clear|unschedule|null)$/i;
|
|
24922
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24923
|
+
function isLinearNoneToken(value) {
|
|
24924
|
+
if (value == null) return true;
|
|
24925
|
+
const s = value.trim();
|
|
24926
|
+
return !s || LINEAR_NONE_RE.test(s);
|
|
24927
|
+
}
|
|
24928
|
+
function resolveLinearLabel(labels, name) {
|
|
24929
|
+
const s = name.trim();
|
|
24930
|
+
if (!s) throw new Error("Linear label is empty");
|
|
24931
|
+
const lower = s.toLowerCase();
|
|
24932
|
+
const found = labels.find((x) => x.id === s) || labels.find((x) => x.name.toLowerCase() === lower);
|
|
24933
|
+
if (!found) {
|
|
24934
|
+
const available = labels.map((x) => x.name).filter(Boolean).slice(0, 20).join(", ") || "(none)";
|
|
24935
|
+
throw new Error(`Linear label not found: ${name}. Available: ${available}`);
|
|
24936
|
+
}
|
|
24937
|
+
return found;
|
|
24938
|
+
}
|
|
24939
|
+
function resolveLinearProject(projects, project) {
|
|
24940
|
+
if (isLinearNoneToken(project)) return null;
|
|
24941
|
+
const s = (project ?? "").trim();
|
|
24942
|
+
const lower = s.toLowerCase();
|
|
24943
|
+
const found = projects.find((x) => x.id === s) || projects.find((x) => x.name.toLowerCase() === lower) || projects.find((x) => x.slugId?.toLowerCase() === lower);
|
|
24944
|
+
if (!found) {
|
|
24945
|
+
const available = projects.map((x) => x.name).filter(Boolean).slice(0, 20).join(", ") || "(none)";
|
|
24946
|
+
throw new Error(`Linear project not found: ${project}. Available: ${available}`);
|
|
24947
|
+
}
|
|
24948
|
+
return found.id;
|
|
24949
|
+
}
|
|
24950
|
+
function normalizeLinearRelationType(type) {
|
|
24951
|
+
const s = type.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
24952
|
+
if (s === "blocks" || s === "block") return "blocks";
|
|
24953
|
+
if (s === "blockedby" || s === "blocked") return "blockedBy";
|
|
24954
|
+
if (s === "related" || s === "relates" || s === "relatesto") return "related";
|
|
24955
|
+
if (s === "duplicate" || s === "dup") return "duplicate";
|
|
24956
|
+
if (s === "duplicateof" || s === "original") return "duplicateOf";
|
|
24957
|
+
if (s === "similar") return "similar";
|
|
24958
|
+
throw new Error(
|
|
24959
|
+
`Linear relation type must be blocks, blockedBy, related, duplicate, or duplicateOf (got ${type})`
|
|
24960
|
+
);
|
|
24961
|
+
}
|
|
24962
|
+
function linearRelationCreateInput(thisIssueId, type, relatedIssueId) {
|
|
24963
|
+
const view = normalizeLinearRelationType(type);
|
|
24964
|
+
if (view === "blockedBy") {
|
|
24965
|
+
return { issueId: relatedIssueId, relatedIssueId: thisIssueId, type: "blocks" };
|
|
24966
|
+
}
|
|
24967
|
+
if (view === "duplicateOf") {
|
|
24968
|
+
return { issueId: relatedIssueId, relatedIssueId: thisIssueId, type: "duplicate" };
|
|
24969
|
+
}
|
|
24970
|
+
return { issueId: thisIssueId, relatedIssueId, type: view };
|
|
24971
|
+
}
|
|
24972
|
+
function relationMatches(rel, type, issueRef) {
|
|
24973
|
+
if (rel.type.toLowerCase() !== type.toLowerCase()) return false;
|
|
24974
|
+
const ref = issueRef.trim().toLowerCase();
|
|
24975
|
+
return rel.issue.id.toLowerCase() === ref || rel.issue.identifier.toLowerCase() === ref;
|
|
24976
|
+
}
|
|
24977
|
+
function formatRelationList(relations) {
|
|
24978
|
+
if (!relations.length) return "(none)";
|
|
24979
|
+
return relations.map((r) => `${r.type} ${r.issue.identifier || r.issue.id}`).join(", ");
|
|
24980
|
+
}
|
|
24981
|
+
async function listLinearLabels(opts) {
|
|
24982
|
+
const json = await linearGraphql(LABELS_QUERY, { first: 250 }, opts);
|
|
24983
|
+
return (json.issueLabels?.nodes ?? []).flatMap((node) => {
|
|
24984
|
+
const id = String(node.id ?? "").trim();
|
|
24985
|
+
const name = String(node.name ?? "").trim();
|
|
24986
|
+
if (!id) return [];
|
|
24987
|
+
return [{ id, name }];
|
|
24988
|
+
});
|
|
24989
|
+
}
|
|
24990
|
+
async function listLinearProjects(filter, opts) {
|
|
24991
|
+
const json = await linearGraphql(PROJECTS_QUERY, { first: 50, filter: filter ?? {} }, opts);
|
|
24992
|
+
return (json.projects?.nodes ?? []).flatMap((node) => {
|
|
24993
|
+
const id = String(node.id ?? "").trim();
|
|
24994
|
+
if (!id) return [];
|
|
24995
|
+
return [
|
|
24996
|
+
{
|
|
24997
|
+
id,
|
|
24998
|
+
name: String(node.name ?? ""),
|
|
24999
|
+
...node.slugId ? { slugId: String(node.slugId) } : {}
|
|
25000
|
+
}
|
|
25001
|
+
];
|
|
25002
|
+
});
|
|
25003
|
+
}
|
|
25004
|
+
async function resolveLinearProjectId(project, opts) {
|
|
25005
|
+
if (isLinearNoneToken(project)) return null;
|
|
25006
|
+
const s = (project ?? "").trim();
|
|
25007
|
+
const filter = UUID_RE.test(s) ? { id: { eq: s } } : { name: { eqIgnoreCase: s } };
|
|
25008
|
+
const matched = await listLinearProjects(filter, opts);
|
|
25009
|
+
try {
|
|
25010
|
+
return resolveLinearProject(matched, s);
|
|
25011
|
+
} catch {
|
|
25012
|
+
if (UUID_RE.test(s)) return s;
|
|
25013
|
+
const listed = await listLinearProjects(void 0, opts);
|
|
25014
|
+
return resolveLinearProject(listed, s);
|
|
25015
|
+
}
|
|
25016
|
+
}
|
|
25017
|
+
async function resolveLinearLabelIds(names, opts) {
|
|
25018
|
+
if (names.length === 0) return [];
|
|
25019
|
+
const labels = await listLinearLabels(opts);
|
|
25020
|
+
return names.map((name) => {
|
|
25021
|
+
const s = name.trim();
|
|
25022
|
+
try {
|
|
25023
|
+
return resolveLinearLabel(labels, s).id;
|
|
25024
|
+
} catch (error) {
|
|
25025
|
+
if (UUID_RE.test(s)) return s;
|
|
25026
|
+
throw error;
|
|
25027
|
+
}
|
|
25028
|
+
});
|
|
25029
|
+
}
|
|
25030
|
+
async function applyLinearIssueRelations(issue, add, remove, opts) {
|
|
25031
|
+
const current = [...issue.relations];
|
|
25032
|
+
for (const rel of remove ?? []) {
|
|
25033
|
+
const type = normalizeLinearRelationType(rel.type);
|
|
25034
|
+
const issueRef = rel.issue.trim();
|
|
25035
|
+
if (!issueRef) throw new Error("Linear removeRelations.issue is required (uuid or ENG-123)");
|
|
25036
|
+
const match = current.find((r) => relationMatches(r, type, issueRef));
|
|
25037
|
+
if (!match?.id) {
|
|
25038
|
+
throw new Error(
|
|
25039
|
+
`Linear relation not found: ${type} ${issueRef}. Existing: ${formatRelationList(current)}`
|
|
25040
|
+
);
|
|
25041
|
+
}
|
|
25042
|
+
const json = await linearGraphql(ISSUE_RELATION_DELETE, { id: match.id }, opts);
|
|
25043
|
+
if (!json.issueRelationDelete?.success) {
|
|
25044
|
+
throw new Error(`Linear issueRelationDelete failed (${type} ${issueRef})`);
|
|
25045
|
+
}
|
|
25046
|
+
const idx = current.indexOf(match);
|
|
25047
|
+
if (idx >= 0) current.splice(idx, 1);
|
|
25048
|
+
}
|
|
25049
|
+
for (const rel of add ?? []) {
|
|
25050
|
+
const type = normalizeLinearRelationType(rel.type);
|
|
25051
|
+
const issueRef = rel.issue.trim();
|
|
25052
|
+
if (!issueRef) throw new Error("Linear relations.issue is required (uuid or ENG-123)");
|
|
25053
|
+
if (current.some((r) => relationMatches(r, type, issueRef))) continue;
|
|
25054
|
+
const related = await getLinearIssue(issueRef, opts);
|
|
25055
|
+
const input = linearRelationCreateInput(issue.id, type, related.id);
|
|
25056
|
+
const json = await linearGraphql(ISSUE_RELATION_CREATE, { input }, opts);
|
|
25057
|
+
if (!json.issueRelationCreate?.success) {
|
|
25058
|
+
throw new Error(`Linear issueRelationCreate failed (${type} ${related.identifier})`);
|
|
25059
|
+
}
|
|
25060
|
+
current.push({ type, issue: related, id: void 0 });
|
|
25061
|
+
}
|
|
25062
|
+
}
|
|
24771
25063
|
function normalizePriority(priority) {
|
|
24772
25064
|
if (priority == null) return void 0;
|
|
24773
25065
|
if (!Number.isInteger(priority) || priority < 0 || priority > 4) {
|
|
@@ -24775,7 +25067,6 @@ function normalizePriority(priority) {
|
|
|
24775
25067
|
}
|
|
24776
25068
|
return priority;
|
|
24777
25069
|
}
|
|
24778
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24779
25070
|
function assigneeFilterKey(assignee) {
|
|
24780
25071
|
return (assignee ?? "").trim();
|
|
24781
25072
|
}
|
|
@@ -24988,8 +25279,10 @@ async function updateLinearIssue(input, opts) {
|
|
|
24988
25279
|
if (input.description !== void 0) {
|
|
24989
25280
|
mutationInput.description = input.description;
|
|
24990
25281
|
}
|
|
24991
|
-
const
|
|
24992
|
-
const
|
|
25282
|
+
const addRelations = input.relations?.length ? input.relations : void 0;
|
|
25283
|
+
const removeRelations = input.removeRelations?.length ? input.removeRelations : void 0;
|
|
25284
|
+
const needsIssue = Boolean(input.state?.trim()) || input.cycle !== void 0 || Boolean(addRelations) || Boolean(removeRelations);
|
|
25285
|
+
const existing = needsIssue ? await getLinearIssue(issueId, opts) : void 0;
|
|
24993
25286
|
if (input.state?.trim()) {
|
|
24994
25287
|
const { teams } = await listLinearTeams(opts);
|
|
24995
25288
|
const teamRef = existing?.team?.key || existing?.team?.id;
|
|
@@ -25022,14 +25315,44 @@ async function updateLinearIssue(input, opts) {
|
|
|
25022
25315
|
if (input.priority !== void 0) {
|
|
25023
25316
|
mutationInput.priority = normalizePriority(input.priority);
|
|
25024
25317
|
}
|
|
25025
|
-
if (
|
|
25026
|
-
|
|
25318
|
+
if (input.labels !== void 0) {
|
|
25319
|
+
mutationInput.labelIds = await resolveLinearLabelIds(input.labels, opts);
|
|
25027
25320
|
}
|
|
25028
|
-
|
|
25029
|
-
|
|
25030
|
-
throw new Error("Linear issueUpdate failed");
|
|
25321
|
+
if (input.project !== void 0) {
|
|
25322
|
+
mutationInput.projectId = await resolveLinearProjectId(input.project, opts);
|
|
25031
25323
|
}
|
|
25032
|
-
|
|
25324
|
+
if (input.parent !== void 0) {
|
|
25325
|
+
if (isLinearNoneToken(input.parent)) {
|
|
25326
|
+
mutationInput.parentId = null;
|
|
25327
|
+
} else {
|
|
25328
|
+
const parent = await getLinearIssue((input.parent ?? "").trim(), opts);
|
|
25329
|
+
mutationInput.parentId = parent.id;
|
|
25330
|
+
}
|
|
25331
|
+
}
|
|
25332
|
+
const hasIssueFields = Object.keys(mutationInput).length > 0;
|
|
25333
|
+
const hasRelationWrites = Boolean(addRelations || removeRelations);
|
|
25334
|
+
if (!hasIssueFields && !hasRelationWrites) {
|
|
25335
|
+
throw new Error(
|
|
25336
|
+
"linear_update_issue needs at least one of title, description, state, assignee, priority, cycle, labels, project, parent, relations, removeRelations"
|
|
25337
|
+
);
|
|
25338
|
+
}
|
|
25339
|
+
let updated;
|
|
25340
|
+
if (hasIssueFields) {
|
|
25341
|
+
const json = await linearGraphql(ISSUE_UPDATE, { id: issueId, input: mutationInput }, opts);
|
|
25342
|
+
if (!json.issueUpdate?.success || !json.issueUpdate.issue) {
|
|
25343
|
+
throw new Error("Linear issueUpdate failed");
|
|
25344
|
+
}
|
|
25345
|
+
updated = mapIssue(json.issueUpdate.issue);
|
|
25346
|
+
}
|
|
25347
|
+
if (hasRelationWrites) {
|
|
25348
|
+
const base = updated ?? existing;
|
|
25349
|
+
if (!base) {
|
|
25350
|
+
throw new Error(`Linear issue not found: ${issueId}`);
|
|
25351
|
+
}
|
|
25352
|
+
await applyLinearIssueRelations(base, addRelations, removeRelations, opts);
|
|
25353
|
+
return getLinearIssue(base.id || issueId, opts);
|
|
25354
|
+
}
|
|
25355
|
+
return updated;
|
|
25033
25356
|
}
|
|
25034
25357
|
async function commentLinearIssue(input, opts) {
|
|
25035
25358
|
const issueId = input.id.trim();
|
|
@@ -25251,13 +25574,78 @@ async function commentGitHubIssue(input, opts) {
|
|
|
25251
25574
|
const url = stdout.trim().split(/\s+/).find((part) => /^https?:\/\//i.test(part));
|
|
25252
25575
|
return { body, url };
|
|
25253
25576
|
}
|
|
25577
|
+
function isGitHubNoneToken(value) {
|
|
25578
|
+
if (value == null) return true;
|
|
25579
|
+
const s = value.trim();
|
|
25580
|
+
return !s || /^(none|clear|unschedule|null)$/i.test(s);
|
|
25581
|
+
}
|
|
25582
|
+
function githubRelationEditFlag(type, remove = false) {
|
|
25583
|
+
const s = type.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
25584
|
+
if (s === "blockedby" || s === "blocked" || s === "dependson") {
|
|
25585
|
+
return remove ? "--remove-blocked-by" : "--add-blocked-by";
|
|
25586
|
+
}
|
|
25587
|
+
if (s === "blocks" || s === "blocking") {
|
|
25588
|
+
return remove ? "--remove-blocking" : "--add-blocking";
|
|
25589
|
+
}
|
|
25590
|
+
throw new Error(
|
|
25591
|
+
`GitHub relation type must be blocks or blockedBy (got ${type}). Parent is a separate parent= field.`
|
|
25592
|
+
);
|
|
25593
|
+
}
|
|
25594
|
+
function labelsEqual(a, b) {
|
|
25595
|
+
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
25596
|
+
}
|
|
25597
|
+
function parseProjectTitles(raw) {
|
|
25598
|
+
const out = [];
|
|
25599
|
+
const seen = /* @__PURE__ */ new Set();
|
|
25600
|
+
const push = (name) => {
|
|
25601
|
+
const title = name.trim();
|
|
25602
|
+
const key = title.toLowerCase();
|
|
25603
|
+
if (!title || seen.has(key)) return;
|
|
25604
|
+
seen.add(key);
|
|
25605
|
+
out.push(title);
|
|
25606
|
+
};
|
|
25607
|
+
const fromItems = raw.projectItems ?? raw.projects ?? raw.projectItemsV2;
|
|
25608
|
+
if (Array.isArray(fromItems)) {
|
|
25609
|
+
for (const item of fromItems) {
|
|
25610
|
+
if (typeof item === "string") {
|
|
25611
|
+
push(item);
|
|
25612
|
+
continue;
|
|
25613
|
+
}
|
|
25614
|
+
if (!item || typeof item !== "object") continue;
|
|
25615
|
+
const rec = item;
|
|
25616
|
+
const nested = rec.project && typeof rec.project === "object" ? rec.project : rec;
|
|
25617
|
+
const title = String(nested.title ?? nested.name ?? rec.title ?? rec.name ?? "").trim();
|
|
25618
|
+
if (title) push(title);
|
|
25619
|
+
}
|
|
25620
|
+
}
|
|
25621
|
+
return out;
|
|
25622
|
+
}
|
|
25623
|
+
async function listGitHubIssueProjects(number, cwd, repoArgs) {
|
|
25624
|
+
const result = await gh(
|
|
25625
|
+
["issue", "view", String(number), ...repoArgs, "--json", "projectItems,projects"],
|
|
25626
|
+
cwd,
|
|
25627
|
+
{ reject: false }
|
|
25628
|
+
);
|
|
25629
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) return [];
|
|
25630
|
+
try {
|
|
25631
|
+
const parsed = JSON.parse(result.stdout);
|
|
25632
|
+
return parseProjectTitles(parsed);
|
|
25633
|
+
} catch {
|
|
25634
|
+
return [];
|
|
25635
|
+
}
|
|
25636
|
+
}
|
|
25254
25637
|
async function updateGitHubIssue(input, opts) {
|
|
25255
25638
|
const number = parseGitHubIssueNumber(input.id);
|
|
25256
25639
|
const title = input.title?.trim();
|
|
25257
25640
|
const body = input.body;
|
|
25258
25641
|
const state = input.state?.trim().toLowerCase();
|
|
25259
|
-
|
|
25260
|
-
|
|
25642
|
+
const addRelations = input.relations?.length ? input.relations : void 0;
|
|
25643
|
+
const removeRelations = input.removeRelations?.length ? input.removeRelations : void 0;
|
|
25644
|
+
const hasEditFields = Boolean(title) || body !== void 0 || input.labels !== void 0 || input.project !== void 0 || input.parent !== void 0 || Boolean(addRelations) || Boolean(removeRelations);
|
|
25645
|
+
if (!hasEditFields && !state) {
|
|
25646
|
+
throw new Error(
|
|
25647
|
+
"github_update_issue needs at least one of title, body, state, labels, project, parent, relations, removeRelations"
|
|
25648
|
+
);
|
|
25261
25649
|
}
|
|
25262
25650
|
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
25263
25651
|
if (state === "closed" || state === "close") {
|
|
@@ -25273,10 +25661,46 @@ async function updateGitHubIssue(input, opts) {
|
|
|
25273
25661
|
} else if (state) {
|
|
25274
25662
|
throw new Error(`GitHub issue state must be open or closed (got ${input.state})`);
|
|
25275
25663
|
}
|
|
25276
|
-
|
|
25277
|
-
|
|
25278
|
-
|
|
25279
|
-
|
|
25664
|
+
const args = ["issue", "edit", String(number), ...repoArgs];
|
|
25665
|
+
if (title) args.push("--title", title);
|
|
25666
|
+
if (body !== void 0) args.push("--body", body);
|
|
25667
|
+
if (input.labels !== void 0) {
|
|
25668
|
+
const existing = await getGitHubIssue(String(number), opts);
|
|
25669
|
+
const desired = input.labels.map((name) => name.trim()).filter(Boolean);
|
|
25670
|
+
const add = desired.filter((name) => !existing.labels.some((cur) => labelsEqual(cur, name)));
|
|
25671
|
+
const remove = existing.labels.filter((cur) => !desired.some((name) => labelsEqual(cur, name)));
|
|
25672
|
+
if (add.length) args.push("--add-label", add.join(","));
|
|
25673
|
+
if (remove.length) args.push("--remove-label", remove.join(","));
|
|
25674
|
+
}
|
|
25675
|
+
if (input.project !== void 0) {
|
|
25676
|
+
const current = await listGitHubIssueProjects(number, cwd, repoArgs);
|
|
25677
|
+
if (isGitHubNoneToken(input.project)) {
|
|
25678
|
+
for (const name of current) args.push("--remove-project", name);
|
|
25679
|
+
} else {
|
|
25680
|
+
const wanted = (input.project ?? "").trim();
|
|
25681
|
+
if (!current.some((name) => labelsEqual(name, wanted))) {
|
|
25682
|
+
args.push("--add-project", wanted);
|
|
25683
|
+
}
|
|
25684
|
+
for (const name of current) {
|
|
25685
|
+
if (!labelsEqual(name, wanted)) args.push("--remove-project", name);
|
|
25686
|
+
}
|
|
25687
|
+
}
|
|
25688
|
+
}
|
|
25689
|
+
if (input.parent !== void 0) {
|
|
25690
|
+
if (isGitHubNoneToken(input.parent)) {
|
|
25691
|
+
args.push("--remove-parent");
|
|
25692
|
+
} else {
|
|
25693
|
+
args.push("--parent", String(parseGitHubIssueNumber(input.parent ?? "")));
|
|
25694
|
+
}
|
|
25695
|
+
}
|
|
25696
|
+
for (const rel of removeRelations ?? []) {
|
|
25697
|
+
args.push(githubRelationEditFlag(rel.type, true), String(parseGitHubIssueNumber(rel.issue)));
|
|
25698
|
+
}
|
|
25699
|
+
for (const rel of addRelations ?? []) {
|
|
25700
|
+
args.push(githubRelationEditFlag(rel.type, false), String(parseGitHubIssueNumber(rel.issue)));
|
|
25701
|
+
}
|
|
25702
|
+
const editChanged = args.length > 3 + repoArgs.length;
|
|
25703
|
+
if (editChanged) {
|
|
25280
25704
|
requireGhOk(await gh(args, cwd, { reject: false }), `GitHub edit #${number}`);
|
|
25281
25705
|
}
|
|
25282
25706
|
return getGitHubIssue(String(number), opts);
|
|
@@ -26958,14 +27382,23 @@ function registerAbleTimeTools(server) {
|
|
|
26958
27382
|
}
|
|
26959
27383
|
}
|
|
26960
27384
|
);
|
|
27385
|
+
const relationInputSchema = import_zod4.z.object({
|
|
27386
|
+
type: import_zod4.z.string().describe("blockedBy (this waits on issue) or blocks (issue waits on this)."),
|
|
27387
|
+
issue: import_zod4.z.string().describe("Related task id or reference (CRM-232).")
|
|
27388
|
+
});
|
|
26961
27389
|
server.tool(
|
|
26962
27390
|
"abletime_update_task",
|
|
26963
|
-
|
|
27391
|
+
'Update an AbleTime task (id or CRM-232). Pass title, description, state, labels, project, parent, and/or relations. labels replaces tags. project is name or id (tasks stay in a project). parent is CRM-232 or "none". relations sets the wait-on dependency (blockedBy/blocks); removeRelations clears it. When reviewing a PR or ticket, wait until they type a next step before changing the task \u2014 do not ask_user after the review. The author is notified.',
|
|
26964
27392
|
{
|
|
26965
27393
|
id: import_zod4.z.string(),
|
|
26966
27394
|
title: import_zod4.z.string().optional(),
|
|
26967
27395
|
description: import_zod4.z.string().optional(),
|
|
26968
|
-
state: import_zod4.z.string().optional()
|
|
27396
|
+
state: import_zod4.z.string().optional(),
|
|
27397
|
+
labels: import_zod4.z.array(import_zod4.z.string()).optional().describe("Replace tags/labels. Empty array clears when the host accepts it."),
|
|
27398
|
+
project: import_zod4.z.string().nullable().optional().describe("Project name or id. AbleTime tasks stay in a project."),
|
|
27399
|
+
parent: import_zod4.z.string().nullable().optional().describe('Parent task id or reference (CRM-232), or "none" to unset.'),
|
|
27400
|
+
relations: import_zod4.z.array(relationInputSchema).optional().describe("Set a wait-on dependency: blockedBy or blocks."),
|
|
27401
|
+
removeRelations: import_zod4.z.array(relationInputSchema).optional().describe("Clear a wait-on dependency.")
|
|
26969
27402
|
},
|
|
26970
27403
|
async (args) => {
|
|
26971
27404
|
try {
|
|
@@ -27100,14 +27533,23 @@ function registerGithubIssueTools(server) {
|
|
|
27100
27533
|
}
|
|
27101
27534
|
}
|
|
27102
27535
|
);
|
|
27536
|
+
const relationInputSchema = import_zod5.z.object({
|
|
27537
|
+
type: import_zod5.z.string().describe("blocks or blockedBy (this issue's view)."),
|
|
27538
|
+
issue: import_zod5.z.string().describe("Related issue #123 or URL.")
|
|
27539
|
+
});
|
|
27103
27540
|
server.tool(
|
|
27104
27541
|
"github_update_issue",
|
|
27105
|
-
|
|
27542
|
+
'Update a GitHub issue (#123). Pass title, body, state (open|closed), labels, project, parent, and/or relations. labels replaces all names (pass [] to clear). project is a Project title, or "none" to remove. parent is #123 or "none". relations adds blockedBy/blocks; removeRelations drops them (`gh` 2.94+). When reviewing a PR or ticket, wait until they type a next step before changing the issue \u2014 do not ask_user after the review. The author is notified.',
|
|
27106
27543
|
{
|
|
27107
27544
|
id: import_zod5.z.string(),
|
|
27108
27545
|
title: import_zod5.z.string().optional(),
|
|
27109
27546
|
body: import_zod5.z.string().optional(),
|
|
27110
27547
|
state: import_zod5.z.string().optional().describe("open or closed"),
|
|
27548
|
+
labels: import_zod5.z.array(import_zod5.z.string()).optional().describe("Replace all labels. Empty array clears. Call github_get_issue first to keep existing."),
|
|
27549
|
+
project: import_zod5.z.string().nullable().optional().describe('GitHub Project title, or "none" to remove current projects.'),
|
|
27550
|
+
parent: import_zod5.z.string().nullable().optional().describe('Parent issue #123 or URL, or "none" to unset.'),
|
|
27551
|
+
relations: import_zod5.z.array(relationInputSchema).optional().describe("Add blockedBy or blocks relations (issue #123 or URL)."),
|
|
27552
|
+
removeRelations: import_zod5.z.array(relationInputSchema).optional().describe("Remove blockedBy or blocks relations."),
|
|
27111
27553
|
repoPath: repoPathSchema
|
|
27112
27554
|
},
|
|
27113
27555
|
async (args) => {
|
|
@@ -27237,9 +27679,13 @@ function registerLinearTools(server) {
|
|
|
27237
27679
|
}
|
|
27238
27680
|
}
|
|
27239
27681
|
);
|
|
27682
|
+
const relationInputSchema = import_zod6.z.object({
|
|
27683
|
+
type: import_zod6.z.string().describe("blocks, blockedBy, related, duplicate, or duplicateOf (this issue's view)."),
|
|
27684
|
+
issue: import_zod6.z.string().describe("Related issue uuid or identifier (ENG-123).")
|
|
27685
|
+
});
|
|
27240
27686
|
server.tool(
|
|
27241
27687
|
"linear_update_issue",
|
|
27242
|
-
'Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, priority, and/or
|
|
27688
|
+
'Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, priority, cycle, labels, project, parent, and/or relations. Cycle/project/parent: name or id, or "none" to clear. labels replaces all names (pass [] to clear; get the issue first to keep existing). relations adds; removeRelations removes by type + issue. When reviewing a PR or ticket, wait until they type a next step before changing the ticket \u2014 do not ask_user after the review. The author is notified.',
|
|
27243
27689
|
{
|
|
27244
27690
|
id: import_zod6.z.string(),
|
|
27245
27691
|
title: import_zod6.z.string().optional(),
|
|
@@ -27247,7 +27693,12 @@ function registerLinearTools(server) {
|
|
|
27247
27693
|
state: import_zod6.z.string().optional(),
|
|
27248
27694
|
assignee: import_zod6.z.string().nullable().optional(),
|
|
27249
27695
|
priority: prioritySchema,
|
|
27250
|
-
cycle: import_zod6.z.string().nullable().optional().describe('Cycle name, number, "current"/"active", or "none" to unschedule.')
|
|
27696
|
+
cycle: import_zod6.z.string().nullable().optional().describe('Cycle name, number, "current"/"active", or "none" to unschedule.'),
|
|
27697
|
+
labels: import_zod6.z.array(import_zod6.z.string()).optional().describe("Replace all labels (names or ids). Empty array clears. Call linear_get_issue first to keep existing."),
|
|
27698
|
+
project: import_zod6.z.string().nullable().optional().describe('Project name, id, slug, or "none" to unassign.'),
|
|
27699
|
+
parent: import_zod6.z.string().nullable().optional().describe('Parent issue uuid or identifier (ENG-123), or "none" to unset.'),
|
|
27700
|
+
relations: import_zod6.z.array(relationInputSchema).optional().describe("Add relations from this issue's view (blocks, blockedBy, related, duplicate, duplicateOf)."),
|
|
27701
|
+
removeRelations: import_zod6.z.array(relationInputSchema).optional().describe("Remove relations by type + related issue (uuid or ENG-123).")
|
|
27251
27702
|
},
|
|
27252
27703
|
async (args) => {
|
|
27253
27704
|
try {
|
|
@@ -31354,6 +31805,8 @@ init_outbound_watch();
|
|
|
31354
31805
|
// Annotate the CommonJS export names for ESM import in node:
|
|
31355
31806
|
0 && (module.exports = {
|
|
31356
31807
|
ABLETIME_MCP_PATH,
|
|
31808
|
+
ABLETIME_MCP_PM_PATH,
|
|
31809
|
+
ABLETIME_PM_TOOL_NAMES,
|
|
31357
31810
|
AGENT_GIT_ACTIONS,
|
|
31358
31811
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
31359
31812
|
ATTACHMENTS_DIR,
|
|
@@ -31443,8 +31896,10 @@ init_outbound_watch();
|
|
|
31443
31896
|
WORKTREE_GITHUB_MCP_TOOLS,
|
|
31444
31897
|
WORKTREE_LINEAR_MCP_TOOLS,
|
|
31445
31898
|
WORKTREE_MCP_TOOLS,
|
|
31899
|
+
ableTimeDependencyTarget,
|
|
31446
31900
|
abletimeMcpRequest,
|
|
31447
31901
|
abletimeMcpUrl,
|
|
31902
|
+
abletimeToolUsesPm,
|
|
31448
31903
|
ackSlackInboundSeen,
|
|
31449
31904
|
addBoardPin,
|
|
31450
31905
|
addPrStackLayer,
|
|
@@ -31717,6 +32172,7 @@ init_outbound_watch();
|
|
|
31717
32172
|
git,
|
|
31718
32173
|
githubAgentGitEnv,
|
|
31719
32174
|
githubHttpsInsteadOfEntries,
|
|
32175
|
+
githubRelationEditFlag,
|
|
31720
32176
|
globalAgentCwd,
|
|
31721
32177
|
groupHomeBoardWorktrees,
|
|
31722
32178
|
handleSlackInbound,
|
|
@@ -31766,6 +32222,7 @@ init_outbound_watch();
|
|
|
31766
32222
|
isInternalAgentStatusText,
|
|
31767
32223
|
isIssueSourceConnected,
|
|
31768
32224
|
isLinearConnected,
|
|
32225
|
+
isLinearNoneToken,
|
|
31769
32226
|
isLinearOAuthCancelled,
|
|
31770
32227
|
isOptionalServiceId,
|
|
31771
32228
|
isOrchestratorCapableAgent,
|
|
@@ -31803,6 +32260,7 @@ init_outbound_watch();
|
|
|
31803
32260
|
linearGraphql,
|
|
31804
32261
|
linearOAuthAuthorizeUrl,
|
|
31805
32262
|
linearOAuthCredentials,
|
|
32263
|
+
linearRelationCreateInput,
|
|
31806
32264
|
linearTicketIdFromThread,
|
|
31807
32265
|
listAbleTimeAssignedIssues,
|
|
31808
32266
|
listAbleTimeProjects,
|
|
@@ -31871,6 +32329,7 @@ init_outbound_watch();
|
|
|
31871
32329
|
nonInteractiveGitProcessEnv,
|
|
31872
32330
|
normalizeAbleTimeHost,
|
|
31873
32331
|
normalizeCodeSelection,
|
|
32332
|
+
normalizeLinearRelationType,
|
|
31874
32333
|
normalizeParseResult,
|
|
31875
32334
|
normalizeServiceOrigin,
|
|
31876
32335
|
normalizeThinkingEffort,
|
|
@@ -31964,6 +32423,8 @@ init_outbound_watch();
|
|
|
31964
32423
|
resolveGithubAgentToken,
|
|
31965
32424
|
resolveGithubRepoSlug,
|
|
31966
32425
|
resolveLinearCycle,
|
|
32426
|
+
resolveLinearLabel,
|
|
32427
|
+
resolveLinearProject,
|
|
31967
32428
|
resolveLinearState,
|
|
31968
32429
|
resolveLinearTeam,
|
|
31969
32430
|
resolveLoginCommand,
|