@sideboard-ai/core 0.1.189 → 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/agents/cursor-runner.js +1 -1
- package/dist/{agents-WH3TW5OP.js → agents-5OBZETUV.js} +1 -1
- package/dist/{agents-XPWKGOVT.js → agents-DTPVTDB5.js} +2 -2
- package/dist/{chunk-TEJU6UJY.js → chunk-4M2AK3O5.js} +126 -20
- package/dist/{chunk-U6FCBOS7.js → chunk-5RRAKGPK.js} +9 -9
- package/dist/{chunk-ZSFQLR7Y.js → chunk-FY64F2ZQ.js} +14 -6
- package/dist/{chunk-QU663NPO.js → chunk-GBWKMWUQ.js} +14 -6
- package/dist/{chunk-LE5OZCKB.js → chunk-GPSIZGEZ.js} +7 -7
- package/dist/{chunk-ISOCLQEX.js → chunk-OLDZXO53.js} +1 -1
- package/dist/{chunk-KEVY4GEK.js → chunk-UBQIELFS.js} +123 -20
- package/dist/index.cjs +536 -55
- package/dist/index.d.cts +90 -5
- package/dist/index.d.ts +90 -5
- package/dist/index.js +393 -29
- package/dist/mcp/run-stdio.cjs +516 -55
- package/dist/mcp/run-stdio.js +378 -28
- package/dist/{orchestrator-EVDOHS2S.js → orchestrator-2X6LYBQI.js} +2 -2
- package/dist/{orchestrator-CWBYGHAT.js → orchestrator-F3CXDBZB.js} +3 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8687,13 +8687,21 @@ function looksLikeHugeToolResultDump(text7) {
|
|
|
8687
8687
|
if (t.length < 4e3) return false;
|
|
8688
8688
|
return looksLikeMinifiedJsDump(t);
|
|
8689
8689
|
}
|
|
8690
|
+
function looksLikeJobWaitResult(rec) {
|
|
8691
|
+
if (typeof rec.delta === "string") {
|
|
8692
|
+
return typeof rec.status === "string" || typeof rec.id === "string" || typeof rec.stillRunning === "boolean";
|
|
8693
|
+
}
|
|
8694
|
+
if (typeof rec.stopped === "boolean") {
|
|
8695
|
+
return typeof rec.id === "string" || typeof rec.status === "string";
|
|
8696
|
+
}
|
|
8697
|
+
return rec.started === true || rec.reason === "already-running";
|
|
8698
|
+
}
|
|
8690
8699
|
function clipJobWaitResultJson(content) {
|
|
8691
8700
|
const trimmed = content.trim();
|
|
8692
8701
|
if (!trimmed.startsWith("{")) return void 0;
|
|
8693
8702
|
try {
|
|
8694
8703
|
const rec = JSON.parse(trimmed);
|
|
8695
|
-
|
|
8696
|
-
if (!isJob) return void 0;
|
|
8704
|
+
if (!looksLikeJobWaitResult(rec)) return void 0;
|
|
8697
8705
|
if (typeof rec.delta === "string" && rec.delta.length > 3500) {
|
|
8698
8706
|
rec.delta = `\u2026(truncated ${rec.delta.length} chars)
|
|
8699
8707
|
${rec.delta.slice(-3500)}`;
|
|
@@ -8859,7 +8867,7 @@ function humanizeAgentFailDetail(detail) {
|
|
|
8859
8867
|
return /ran out of memory/i.test(raw) ? raw : V8_OOM_SUMMARY;
|
|
8860
8868
|
}
|
|
8861
8869
|
if (/homebrew node \+ shared libuv|Cellar\/(?:libuv|node)|libuv\.\d+\.dylib/i.test(raw)) {
|
|
8862
|
-
return /brew install node@22/i.test(raw) ? raw : `${raw} \u2014 install Node
|
|
8870
|
+
return /brew install node@(?:22|24)/i.test(raw) ? raw : `${raw} \u2014 install Node 24 LTS (\`brew install node@24\`) and retry.`;
|
|
8863
8871
|
}
|
|
8864
8872
|
if (/uv_run|spineventloopinternal|uv__io_poll|cursor runner crashed in node/i.test(lower)) {
|
|
8865
8873
|
return /retry the turn/i.test(raw) ? raw : BUNDLED_NODE_CRASH_SUMMARY;
|
|
@@ -8937,7 +8945,7 @@ var init_error_detail = __esm({
|
|
|
8937
8945
|
"use strict";
|
|
8938
8946
|
NODE_VERSION_FOOTER = /^Node\.js v\d+/i;
|
|
8939
8947
|
NESTED_ELECTRON_SUMMARY = "Cursor local agent crashed at Electron startup (nested Chromium / HasCustomHostObject)";
|
|
8940
|
-
HOMEBREW_LIBUV_SUMMARY = "Cursor runner crashed in Node (Homebrew Node + shared libuv). Install Node
|
|
8948
|
+
HOMEBREW_LIBUV_SUMMARY = "Cursor runner crashed in Node (Homebrew Node + shared libuv). Install Node 24 LTS (`brew install node@24`) and retry.";
|
|
8941
8949
|
BUNDLED_NODE_CRASH_SUMMARY = "Cursor runner crashed in Node. Retry the turn.";
|
|
8942
8950
|
V8_OOM_SUMMARY = "Agent ran out of memory (JavaScript heap). If it keeps happening, exclude large files from the project folder.";
|
|
8943
8951
|
MINIFIED_DUMP_SUMMARY = "Cursor local agent crashed during startup (truncated crash dump)";
|
|
@@ -10807,7 +10815,7 @@ function parseNodeMajor(version) {
|
|
|
10807
10815
|
function scoreNodeForAgentRuntime(candidate) {
|
|
10808
10816
|
const major = parseNodeMajor(candidate.version);
|
|
10809
10817
|
if (major == null) return Number.NEGATIVE_INFINITY;
|
|
10810
|
-
if (major <
|
|
10818
|
+
if (major < 22) return major - 100;
|
|
10811
10819
|
const posix = candidate.path.replace(/\\/g, "/");
|
|
10812
10820
|
let score = 0;
|
|
10813
10821
|
if (major % 2 === 0) {
|
|
@@ -10968,7 +10976,7 @@ var init_node_launch = __esm({
|
|
|
10968
10976
|
init_packaged_runtime();
|
|
10969
10977
|
AGENT_RUNNER_MAX_OLD_SPACE_MB = 8192;
|
|
10970
10978
|
MAX_OLD_SPACE_FLAG = /(?:^|\s)(--max[-_]old[-_]space[-_]size)(?:[= ](\d+))?(?=\s|$)/;
|
|
10971
|
-
PREFERRED_LTS_MAJORS = [24, 22
|
|
10979
|
+
PREFERRED_LTS_MAJORS = [24, 22];
|
|
10972
10980
|
}
|
|
10973
10981
|
});
|
|
10974
10982
|
|
|
@@ -16880,13 +16888,17 @@ var init_home_board = __esm({
|
|
|
16880
16888
|
});
|
|
16881
16889
|
|
|
16882
16890
|
// src/integrations/abletime-mcp.ts
|
|
16891
|
+
function abletimeToolUsesPm(name) {
|
|
16892
|
+
return ABLETIME_PM_TOOL_NAMES.includes(name);
|
|
16893
|
+
}
|
|
16883
16894
|
function normalizeAbleTimeHost(raw) {
|
|
16884
16895
|
const trimmed = raw?.trim() || DEFAULT_ABLETIME_HOST;
|
|
16885
16896
|
const withScheme = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
16886
16897
|
return withScheme.replace(/\/+$/, "");
|
|
16887
16898
|
}
|
|
16888
|
-
function abletimeMcpUrl(host) {
|
|
16889
|
-
|
|
16899
|
+
function abletimeMcpUrl(host, opts) {
|
|
16900
|
+
const path2 = opts?.pm ? ABLETIME_MCP_PM_PATH : ABLETIME_MCP_PATH;
|
|
16901
|
+
return `${normalizeAbleTimeHost(host)}${path2}`;
|
|
16890
16902
|
}
|
|
16891
16903
|
function rewriteAbleTimeError(message) {
|
|
16892
16904
|
if (/INTEGRATION_AGENT_ACCESS_DISABLED|agent access/i.test(message)) {
|
|
@@ -16964,7 +16976,8 @@ async function abletimeMcpRequest(method, params, opts) {
|
|
|
16964
16976
|
throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
|
|
16965
16977
|
}
|
|
16966
16978
|
const url = abletimeMcpUrl(
|
|
16967
|
-
opts?.host ?? (opts?.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost())
|
|
16979
|
+
opts?.host ?? (opts?.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost()),
|
|
16980
|
+
{ pm: opts?.pm }
|
|
16968
16981
|
);
|
|
16969
16982
|
const res = await httpFetch(url, {
|
|
16970
16983
|
method: "POST",
|
|
@@ -17028,7 +17041,8 @@ async function callAbleTimeTool(name, args = {}, opts) {
|
|
|
17028
17041
|
if (!opts?.token && !isAbleTimeConnected()) {
|
|
17029
17042
|
throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
|
|
17030
17043
|
}
|
|
17031
|
-
const
|
|
17044
|
+
const requestOpts = { ...opts, pm: opts?.pm ?? abletimeToolUsesPm(name) };
|
|
17045
|
+
const call = () => abletimeMcpRequest("tools/call", { name, arguments: args }, requestOpts).then((result) => {
|
|
17032
17046
|
return unwrapToolResult(result);
|
|
17033
17047
|
});
|
|
17034
17048
|
try {
|
|
@@ -17036,13 +17050,13 @@ async function callAbleTimeTool(name, args = {}, opts) {
|
|
|
17036
17050
|
} catch (err) {
|
|
17037
17051
|
const message = err instanceof Error ? err.message : String(err);
|
|
17038
17052
|
if (/initialize|session|not initialized|-32000/i.test(message)) {
|
|
17039
|
-
await initializeIfNeeded(
|
|
17053
|
+
await initializeIfNeeded(requestOpts);
|
|
17040
17054
|
return await call();
|
|
17041
17055
|
}
|
|
17042
17056
|
throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
|
|
17043
17057
|
}
|
|
17044
17058
|
}
|
|
17045
|
-
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;
|
|
17046
17060
|
var init_abletime_mcp = __esm({
|
|
17047
17061
|
"src/integrations/abletime-mcp.ts"() {
|
|
17048
17062
|
"use strict";
|
|
@@ -17050,13 +17064,27 @@ var init_abletime_mcp = __esm({
|
|
|
17050
17064
|
init_app_settings();
|
|
17051
17065
|
DEFAULT_ABLETIME_HOST = "https://track.abletime.com";
|
|
17052
17066
|
ABLETIME_MCP_PATH = "/api/public/v2/mcp";
|
|
17067
|
+
ABLETIME_MCP_PM_PATH = "/api/public/v2/mcp/pm";
|
|
17053
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
|
+
];
|
|
17054
17081
|
}
|
|
17055
17082
|
});
|
|
17056
17083
|
|
|
17057
17084
|
// src/integrations/abletime.ts
|
|
17058
17085
|
var abletime_exports = {};
|
|
17059
17086
|
__export(abletime_exports, {
|
|
17087
|
+
ableTimeDependencyTarget: () => ableTimeDependencyTarget,
|
|
17060
17088
|
commentAbleTimeTask: () => commentAbleTimeTask,
|
|
17061
17089
|
createAbleTimeTask: () => createAbleTimeTask,
|
|
17062
17090
|
ensureAbleTimeTask: () => ensureAbleTimeTask,
|
|
@@ -17313,39 +17341,122 @@ async function commentAbleTimeTask(input, opts) {
|
|
|
17313
17341
|
body: rec ? firstString(rec, ["body", "text", "comment"]) || body : body
|
|
17314
17342
|
};
|
|
17315
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
|
+
}
|
|
17316
17387
|
async function updateAbleTimeTask(input, opts) {
|
|
17317
17388
|
const id = input.id.trim();
|
|
17318
17389
|
if (!id) throw new Error("AbleTime task id is required");
|
|
17319
17390
|
const title = input.title?.trim();
|
|
17320
17391
|
const description = input.description;
|
|
17321
17392
|
const state = input.state?.trim();
|
|
17322
|
-
|
|
17323
|
-
|
|
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
|
+
);
|
|
17324
17429
|
}
|
|
17325
17430
|
if (state) {
|
|
17326
17431
|
await callAbleTimeTool(
|
|
17327
17432
|
"set_task_state",
|
|
17328
|
-
{ id,
|
|
17433
|
+
{ ...taskIdArgs(id), state },
|
|
17329
17434
|
opts
|
|
17330
17435
|
).catch(async () => {
|
|
17331
17436
|
await callAbleTimeTool(
|
|
17332
17437
|
"update_task",
|
|
17333
|
-
{
|
|
17438
|
+
{ ...updateArgs, state },
|
|
17334
17439
|
opts
|
|
17335
17440
|
);
|
|
17336
17441
|
});
|
|
17337
17442
|
}
|
|
17338
|
-
if (
|
|
17339
|
-
await callAbleTimeTool(
|
|
17340
|
-
|
|
17341
|
-
|
|
17342
|
-
|
|
17343
|
-
|
|
17344
|
-
|
|
17345
|
-
|
|
17346
|
-
|
|
17347
|
-
|
|
17348
|
-
);
|
|
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);
|
|
17349
17460
|
}
|
|
17350
17461
|
return getAbleTimeTask(id, opts);
|
|
17351
17462
|
}
|
|
@@ -20023,17 +20134,17 @@ function formatIssueToolsDirective(opts) {
|
|
|
20023
20134
|
];
|
|
20024
20135
|
if (opts.linear) {
|
|
20025
20136
|
lines.push(
|
|
20026
|
-
"- 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."
|
|
20027
20138
|
);
|
|
20028
20139
|
}
|
|
20029
20140
|
if (github) {
|
|
20030
20141
|
lines.push(
|
|
20031
|
-
"- 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`."
|
|
20032
20143
|
);
|
|
20033
20144
|
}
|
|
20034
20145
|
if (opts.abletime) {
|
|
20035
20146
|
lines.push(
|
|
20036
|
-
"- 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)."
|
|
20037
20148
|
);
|
|
20038
20149
|
}
|
|
20039
20150
|
lines.push(
|
|
@@ -23123,6 +23234,8 @@ var init_schedule_runner = __esm({
|
|
|
23123
23234
|
var index_exports = {};
|
|
23124
23235
|
__export(index_exports, {
|
|
23125
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,
|
|
23126
23239
|
AGENT_GIT_ACTIONS: () => AGENT_GIT_ACTIONS,
|
|
23127
23240
|
AGENT_RUNNER_MAX_OLD_SPACE_MB: () => AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
23128
23241
|
ATTACHMENTS_DIR: () => ATTACHMENTS_DIR,
|
|
@@ -23212,8 +23325,10 @@ __export(index_exports, {
|
|
|
23212
23325
|
WORKTREE_GITHUB_MCP_TOOLS: () => WORKTREE_GITHUB_MCP_TOOLS,
|
|
23213
23326
|
WORKTREE_LINEAR_MCP_TOOLS: () => WORKTREE_LINEAR_MCP_TOOLS,
|
|
23214
23327
|
WORKTREE_MCP_TOOLS: () => WORKTREE_MCP_TOOLS,
|
|
23328
|
+
ableTimeDependencyTarget: () => ableTimeDependencyTarget,
|
|
23215
23329
|
abletimeMcpRequest: () => abletimeMcpRequest,
|
|
23216
23330
|
abletimeMcpUrl: () => abletimeMcpUrl,
|
|
23331
|
+
abletimeToolUsesPm: () => abletimeToolUsesPm,
|
|
23217
23332
|
ackSlackInboundSeen: () => ackSlackInboundSeen,
|
|
23218
23333
|
addBoardPin: () => addBoardPin,
|
|
23219
23334
|
addPrStackLayer: () => addPrStackLayer,
|
|
@@ -23486,6 +23601,7 @@ __export(index_exports, {
|
|
|
23486
23601
|
git: () => git,
|
|
23487
23602
|
githubAgentGitEnv: () => githubAgentGitEnv,
|
|
23488
23603
|
githubHttpsInsteadOfEntries: () => githubHttpsInsteadOfEntries,
|
|
23604
|
+
githubRelationEditFlag: () => githubRelationEditFlag,
|
|
23489
23605
|
globalAgentCwd: () => globalAgentCwd,
|
|
23490
23606
|
groupHomeBoardWorktrees: () => groupHomeBoardWorktrees,
|
|
23491
23607
|
handleSlackInbound: () => handleSlackInbound,
|
|
@@ -23535,6 +23651,7 @@ __export(index_exports, {
|
|
|
23535
23651
|
isInternalAgentStatusText: () => isInternalAgentStatusText,
|
|
23536
23652
|
isIssueSourceConnected: () => isIssueSourceConnected,
|
|
23537
23653
|
isLinearConnected: () => isLinearConnected,
|
|
23654
|
+
isLinearNoneToken: () => isLinearNoneToken,
|
|
23538
23655
|
isLinearOAuthCancelled: () => isLinearOAuthCancelled,
|
|
23539
23656
|
isOptionalServiceId: () => isOptionalServiceId,
|
|
23540
23657
|
isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
|
|
@@ -23572,6 +23689,7 @@ __export(index_exports, {
|
|
|
23572
23689
|
linearGraphql: () => linearGraphql,
|
|
23573
23690
|
linearOAuthAuthorizeUrl: () => linearOAuthAuthorizeUrl,
|
|
23574
23691
|
linearOAuthCredentials: () => linearOAuthCredentials,
|
|
23692
|
+
linearRelationCreateInput: () => linearRelationCreateInput,
|
|
23575
23693
|
linearTicketIdFromThread: () => linearTicketIdFromThread,
|
|
23576
23694
|
listAbleTimeAssignedIssues: () => listAbleTimeAssignedIssues,
|
|
23577
23695
|
listAbleTimeProjects: () => listAbleTimeProjects,
|
|
@@ -23640,6 +23758,7 @@ __export(index_exports, {
|
|
|
23640
23758
|
nonInteractiveGitProcessEnv: () => nonInteractiveGitProcessEnv,
|
|
23641
23759
|
normalizeAbleTimeHost: () => normalizeAbleTimeHost,
|
|
23642
23760
|
normalizeCodeSelection: () => normalizeCodeSelection,
|
|
23761
|
+
normalizeLinearRelationType: () => normalizeLinearRelationType,
|
|
23643
23762
|
normalizeParseResult: () => normalizeParseResult,
|
|
23644
23763
|
normalizeServiceOrigin: () => normalizeServiceOrigin,
|
|
23645
23764
|
normalizeThinkingEffort: () => normalizeThinkingEffort,
|
|
@@ -23733,6 +23852,8 @@ __export(index_exports, {
|
|
|
23733
23852
|
resolveGithubAgentToken: () => resolveGithubAgentToken,
|
|
23734
23853
|
resolveGithubRepoSlug: () => resolveGithubRepoSlug,
|
|
23735
23854
|
resolveLinearCycle: () => resolveLinearCycle,
|
|
23855
|
+
resolveLinearLabel: () => resolveLinearLabel,
|
|
23856
|
+
resolveLinearProject: () => resolveLinearProject,
|
|
23736
23857
|
resolveLinearState: () => resolveLinearState,
|
|
23737
23858
|
resolveLinearTeam: () => resolveLinearTeam,
|
|
23738
23859
|
resolveLoginCommand: () => resolveLoginCommand,
|
|
@@ -24350,12 +24471,14 @@ var ISSUE_FIELDS = `
|
|
|
24350
24471
|
children(first: 25) { nodes { ${ISSUE_REF_FIELDS} } }
|
|
24351
24472
|
relations(first: 25) {
|
|
24352
24473
|
nodes {
|
|
24474
|
+
id
|
|
24353
24475
|
type
|
|
24354
24476
|
relatedIssue { ${ISSUE_REF_FIELDS} }
|
|
24355
24477
|
}
|
|
24356
24478
|
}
|
|
24357
24479
|
inverseRelations(first: 25) {
|
|
24358
24480
|
nodes {
|
|
24481
|
+
id
|
|
24359
24482
|
type
|
|
24360
24483
|
issue { ${ISSUE_REF_FIELDS} }
|
|
24361
24484
|
}
|
|
@@ -24475,6 +24598,35 @@ mutation SideboardCommentCreate($input: CommentCreateInput!) {
|
|
|
24475
24598
|
}
|
|
24476
24599
|
}
|
|
24477
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
|
+
`;
|
|
24478
24630
|
function rewriteLinearError(message) {
|
|
24479
24631
|
if (/scope|permission|not (authorized|allowed)|unauthorized|insufficient/i.test(message)) {
|
|
24480
24632
|
return `${message} \u2014 Disconnect and Connect Linear in Account settings to grant write access.`;
|
|
@@ -24594,12 +24746,18 @@ function mapRelations(node) {
|
|
|
24594
24746
|
for (const rel of node.relations?.nodes ?? []) {
|
|
24595
24747
|
const issue = mapIssueRef(rel.relatedIssue);
|
|
24596
24748
|
if (!issue) continue;
|
|
24597
|
-
|
|
24749
|
+
const id = rel.id?.trim();
|
|
24750
|
+
out.push({ type: String(rel.type ?? "related"), issue, ...id ? { id } : {} });
|
|
24598
24751
|
}
|
|
24599
24752
|
for (const rel of node.inverseRelations?.nodes ?? []) {
|
|
24600
24753
|
const issue = mapIssueRef(rel.issue);
|
|
24601
24754
|
if (!issue) continue;
|
|
24602
|
-
|
|
24755
|
+
const id = rel.id?.trim();
|
|
24756
|
+
out.push({
|
|
24757
|
+
type: flipLinearRelationType(String(rel.type ?? "related")),
|
|
24758
|
+
issue,
|
|
24759
|
+
...id ? { id } : {}
|
|
24760
|
+
});
|
|
24603
24761
|
}
|
|
24604
24762
|
return out;
|
|
24605
24763
|
}
|
|
@@ -24745,7 +24903,14 @@ function resolveLinearCycle(team, cycle) {
|
|
|
24745
24903
|
}
|
|
24746
24904
|
const lower = s.toLowerCase();
|
|
24747
24905
|
const asNumber = Number.parseInt(s, 10);
|
|
24748
|
-
const
|
|
24906
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24907
|
+
const cycles = [team.activeCycle, ...team.cycles ?? []].filter(
|
|
24908
|
+
(c) => {
|
|
24909
|
+
if (!c?.id || seen.has(c.id)) return false;
|
|
24910
|
+
seen.add(c.id);
|
|
24911
|
+
return true;
|
|
24912
|
+
}
|
|
24913
|
+
);
|
|
24749
24914
|
const found = cycles.find((c) => c.id === s) || cycles.find((c) => c.name.toLowerCase() === lower) || (Number.isInteger(asNumber) ? cycles.find((c) => c.number === asNumber) : void 0);
|
|
24750
24915
|
if (!found) {
|
|
24751
24916
|
const available = cycles.map((c) => c.number != null ? `${c.name} (${c.number})` : c.name).join(", ") || "(none)";
|
|
@@ -24753,6 +24918,148 @@ function resolveLinearCycle(team, cycle) {
|
|
|
24753
24918
|
}
|
|
24754
24919
|
return found.id;
|
|
24755
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
|
+
}
|
|
24756
25063
|
function normalizePriority(priority) {
|
|
24757
25064
|
if (priority == null) return void 0;
|
|
24758
25065
|
if (!Number.isInteger(priority) || priority < 0 || priority > 4) {
|
|
@@ -24760,7 +25067,6 @@ function normalizePriority(priority) {
|
|
|
24760
25067
|
}
|
|
24761
25068
|
return priority;
|
|
24762
25069
|
}
|
|
24763
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24764
25070
|
function assigneeFilterKey(assignee) {
|
|
24765
25071
|
return (assignee ?? "").trim();
|
|
24766
25072
|
}
|
|
@@ -24862,7 +25168,12 @@ async function listLinearCommentsSince(opts) {
|
|
|
24862
25168
|
first,
|
|
24863
25169
|
filter: {
|
|
24864
25170
|
createdAt: { gte: opts.since },
|
|
24865
|
-
issue: buildLinearIssueFilter({
|
|
25171
|
+
issue: buildLinearIssueFilter({
|
|
25172
|
+
assignee,
|
|
25173
|
+
// Search hits may match identifier/comments — do not also require
|
|
25174
|
+
// title/description contains when an allowlist is already applied.
|
|
25175
|
+
query: restrictToIssues ? void 0 : query || void 0
|
|
25176
|
+
})
|
|
24866
25177
|
}
|
|
24867
25178
|
},
|
|
24868
25179
|
opts
|
|
@@ -24968,8 +25279,10 @@ async function updateLinearIssue(input, opts) {
|
|
|
24968
25279
|
if (input.description !== void 0) {
|
|
24969
25280
|
mutationInput.description = input.description;
|
|
24970
25281
|
}
|
|
24971
|
-
const
|
|
24972
|
-
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;
|
|
24973
25286
|
if (input.state?.trim()) {
|
|
24974
25287
|
const { teams } = await listLinearTeams(opts);
|
|
24975
25288
|
const teamRef = existing?.team?.key || existing?.team?.id;
|
|
@@ -25002,14 +25315,44 @@ async function updateLinearIssue(input, opts) {
|
|
|
25002
25315
|
if (input.priority !== void 0) {
|
|
25003
25316
|
mutationInput.priority = normalizePriority(input.priority);
|
|
25004
25317
|
}
|
|
25005
|
-
if (
|
|
25006
|
-
|
|
25318
|
+
if (input.labels !== void 0) {
|
|
25319
|
+
mutationInput.labelIds = await resolveLinearLabelIds(input.labels, opts);
|
|
25320
|
+
}
|
|
25321
|
+
if (input.project !== void 0) {
|
|
25322
|
+
mutationInput.projectId = await resolveLinearProjectId(input.project, opts);
|
|
25323
|
+
}
|
|
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
|
+
}
|
|
25007
25331
|
}
|
|
25008
|
-
const
|
|
25009
|
-
|
|
25010
|
-
|
|
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
|
+
);
|
|
25011
25338
|
}
|
|
25012
|
-
|
|
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;
|
|
25013
25356
|
}
|
|
25014
25357
|
async function commentLinearIssue(input, opts) {
|
|
25015
25358
|
const issueId = input.id.trim();
|
|
@@ -25163,7 +25506,7 @@ async function listGitHubIssueCommentsSince(opts) {
|
|
|
25163
25506
|
}).filter((n) => n != null)
|
|
25164
25507
|
);
|
|
25165
25508
|
if (restrictToIssues && allowed.size === 0) return [];
|
|
25166
|
-
const pageSize =
|
|
25509
|
+
const pageSize = GITHUB_ISSUE_COMMENTS_PAGE_SIZE;
|
|
25167
25510
|
const out = [];
|
|
25168
25511
|
for (let page = 1; page <= GITHUB_ISSUE_COMMENTS_MAX_PAGES; page++) {
|
|
25169
25512
|
const path2 = `repos/${slug}/issues/comments?since=${encodeURIComponent(opts.since)}&sort=created&direction=desc&per_page=${pageSize}&page=${page}`;
|
|
@@ -25231,13 +25574,78 @@ async function commentGitHubIssue(input, opts) {
|
|
|
25231
25574
|
const url = stdout.trim().split(/\s+/).find((part) => /^https?:\/\//i.test(part));
|
|
25232
25575
|
return { body, url };
|
|
25233
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
|
+
}
|
|
25234
25637
|
async function updateGitHubIssue(input, opts) {
|
|
25235
25638
|
const number = parseGitHubIssueNumber(input.id);
|
|
25236
25639
|
const title = input.title?.trim();
|
|
25237
25640
|
const body = input.body;
|
|
25238
25641
|
const state = input.state?.trim().toLowerCase();
|
|
25239
|
-
|
|
25240
|
-
|
|
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
|
+
);
|
|
25241
25649
|
}
|
|
25242
25650
|
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
25243
25651
|
if (state === "closed" || state === "close") {
|
|
@@ -25253,10 +25661,46 @@ async function updateGitHubIssue(input, opts) {
|
|
|
25253
25661
|
} else if (state) {
|
|
25254
25662
|
throw new Error(`GitHub issue state must be open or closed (got ${input.state})`);
|
|
25255
25663
|
}
|
|
25256
|
-
|
|
25257
|
-
|
|
25258
|
-
|
|
25259
|
-
|
|
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) {
|
|
25260
25704
|
requireGhOk(await gh(args, cwd, { reject: false }), `GitHub edit #${number}`);
|
|
25261
25705
|
}
|
|
25262
25706
|
return getGitHubIssue(String(number), opts);
|
|
@@ -26938,14 +27382,23 @@ function registerAbleTimeTools(server) {
|
|
|
26938
27382
|
}
|
|
26939
27383
|
}
|
|
26940
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
|
+
});
|
|
26941
27389
|
server.tool(
|
|
26942
27390
|
"abletime_update_task",
|
|
26943
|
-
|
|
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.',
|
|
26944
27392
|
{
|
|
26945
27393
|
id: import_zod4.z.string(),
|
|
26946
27394
|
title: import_zod4.z.string().optional(),
|
|
26947
27395
|
description: import_zod4.z.string().optional(),
|
|
26948
|
-
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.")
|
|
26949
27402
|
},
|
|
26950
27403
|
async (args) => {
|
|
26951
27404
|
try {
|
|
@@ -27080,14 +27533,23 @@ function registerGithubIssueTools(server) {
|
|
|
27080
27533
|
}
|
|
27081
27534
|
}
|
|
27082
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
|
+
});
|
|
27083
27540
|
server.tool(
|
|
27084
27541
|
"github_update_issue",
|
|
27085
|
-
|
|
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.',
|
|
27086
27543
|
{
|
|
27087
27544
|
id: import_zod5.z.string(),
|
|
27088
27545
|
title: import_zod5.z.string().optional(),
|
|
27089
27546
|
body: import_zod5.z.string().optional(),
|
|
27090
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."),
|
|
27091
27553
|
repoPath: repoPathSchema
|
|
27092
27554
|
},
|
|
27093
27555
|
async (args) => {
|
|
@@ -27217,9 +27679,13 @@ function registerLinearTools(server) {
|
|
|
27217
27679
|
}
|
|
27218
27680
|
}
|
|
27219
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
|
+
});
|
|
27220
27686
|
server.tool(
|
|
27221
27687
|
"linear_update_issue",
|
|
27222
|
-
'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.',
|
|
27223
27689
|
{
|
|
27224
27690
|
id: import_zod6.z.string(),
|
|
27225
27691
|
title: import_zod6.z.string().optional(),
|
|
@@ -27227,7 +27693,12 @@ function registerLinearTools(server) {
|
|
|
27227
27693
|
state: import_zod6.z.string().optional(),
|
|
27228
27694
|
assignee: import_zod6.z.string().nullable().optional(),
|
|
27229
27695
|
priority: prioritySchema,
|
|
27230
|
-
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).")
|
|
27231
27702
|
},
|
|
27232
27703
|
async (args) => {
|
|
27233
27704
|
try {
|
|
@@ -31334,6 +31805,8 @@ init_outbound_watch();
|
|
|
31334
31805
|
// Annotate the CommonJS export names for ESM import in node:
|
|
31335
31806
|
0 && (module.exports = {
|
|
31336
31807
|
ABLETIME_MCP_PATH,
|
|
31808
|
+
ABLETIME_MCP_PM_PATH,
|
|
31809
|
+
ABLETIME_PM_TOOL_NAMES,
|
|
31337
31810
|
AGENT_GIT_ACTIONS,
|
|
31338
31811
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
31339
31812
|
ATTACHMENTS_DIR,
|
|
@@ -31423,8 +31896,10 @@ init_outbound_watch();
|
|
|
31423
31896
|
WORKTREE_GITHUB_MCP_TOOLS,
|
|
31424
31897
|
WORKTREE_LINEAR_MCP_TOOLS,
|
|
31425
31898
|
WORKTREE_MCP_TOOLS,
|
|
31899
|
+
ableTimeDependencyTarget,
|
|
31426
31900
|
abletimeMcpRequest,
|
|
31427
31901
|
abletimeMcpUrl,
|
|
31902
|
+
abletimeToolUsesPm,
|
|
31428
31903
|
ackSlackInboundSeen,
|
|
31429
31904
|
addBoardPin,
|
|
31430
31905
|
addPrStackLayer,
|
|
@@ -31697,6 +32172,7 @@ init_outbound_watch();
|
|
|
31697
32172
|
git,
|
|
31698
32173
|
githubAgentGitEnv,
|
|
31699
32174
|
githubHttpsInsteadOfEntries,
|
|
32175
|
+
githubRelationEditFlag,
|
|
31700
32176
|
globalAgentCwd,
|
|
31701
32177
|
groupHomeBoardWorktrees,
|
|
31702
32178
|
handleSlackInbound,
|
|
@@ -31746,6 +32222,7 @@ init_outbound_watch();
|
|
|
31746
32222
|
isInternalAgentStatusText,
|
|
31747
32223
|
isIssueSourceConnected,
|
|
31748
32224
|
isLinearConnected,
|
|
32225
|
+
isLinearNoneToken,
|
|
31749
32226
|
isLinearOAuthCancelled,
|
|
31750
32227
|
isOptionalServiceId,
|
|
31751
32228
|
isOrchestratorCapableAgent,
|
|
@@ -31783,6 +32260,7 @@ init_outbound_watch();
|
|
|
31783
32260
|
linearGraphql,
|
|
31784
32261
|
linearOAuthAuthorizeUrl,
|
|
31785
32262
|
linearOAuthCredentials,
|
|
32263
|
+
linearRelationCreateInput,
|
|
31786
32264
|
linearTicketIdFromThread,
|
|
31787
32265
|
listAbleTimeAssignedIssues,
|
|
31788
32266
|
listAbleTimeProjects,
|
|
@@ -31851,6 +32329,7 @@ init_outbound_watch();
|
|
|
31851
32329
|
nonInteractiveGitProcessEnv,
|
|
31852
32330
|
normalizeAbleTimeHost,
|
|
31853
32331
|
normalizeCodeSelection,
|
|
32332
|
+
normalizeLinearRelationType,
|
|
31854
32333
|
normalizeParseResult,
|
|
31855
32334
|
normalizeServiceOrigin,
|
|
31856
32335
|
normalizeThinkingEffort,
|
|
@@ -31944,6 +32423,8 @@ init_outbound_watch();
|
|
|
31944
32423
|
resolveGithubAgentToken,
|
|
31945
32424
|
resolveGithubRepoSlug,
|
|
31946
32425
|
resolveLinearCycle,
|
|
32426
|
+
resolveLinearLabel,
|
|
32427
|
+
resolveLinearProject,
|
|
31947
32428
|
resolveLinearState,
|
|
31948
32429
|
resolveLinearTeam,
|
|
31949
32430
|
resolveLoginCommand,
|