@sideboard-ai/core 0.1.187 → 0.1.189
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/agents/cursor-runner.js +1 -1
- package/dist/{agents-TPK2JYL4.js → agents-WH3TW5OP.js} +1 -1
- package/dist/{agents-W3JQANEC.js → agents-XPWKGOVT.js} +2 -2
- package/dist/{chunk-6XOSOIDJ.js → chunk-ISOCLQEX.js} +1 -1
- package/dist/{chunk-S6HYHXFO.js → chunk-LE5OZCKB.js} +45 -10
- package/dist/{chunk-7DBYKEYF.js → chunk-QU663NPO.js} +34 -0
- package/dist/{chunk-P56XKPWT.js → chunk-U6FCBOS7.js} +47 -12
- package/dist/{chunk-NQTKRAUF.js → chunk-ZSFQLR7Y.js} +34 -0
- package/dist/index.cjs +173 -19
- package/dist/index.d.cts +22 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +97 -14
- package/dist/mcp/run-stdio.cjs +169 -19
- package/dist/mcp/run-stdio.js +94 -13
- package/dist/{orchestrator-5CFTLN7B.js → orchestrator-CWBYGHAT.js} +3 -3
- package/dist/{orchestrator-6PZQ7DZS.js → orchestrator-EVDOHS2S.js} +2 -2
- package/package.json +1 -1
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -1610,11 +1610,45 @@ function looksLikeHugeToolResultDump(text7) {
|
|
|
1610
1610
|
if (t.length < 4e3) return false;
|
|
1611
1611
|
return looksLikeMinifiedJsDump(t);
|
|
1612
1612
|
}
|
|
1613
|
+
function clipJobWaitResultJson(content) {
|
|
1614
|
+
const trimmed = content.trim();
|
|
1615
|
+
if (!trimmed.startsWith("{")) return void 0;
|
|
1616
|
+
try {
|
|
1617
|
+
const rec = JSON.parse(trimmed);
|
|
1618
|
+
const isJob = typeof rec.stillRunning === "boolean" || typeof rec.stopped === "boolean" || typeof rec.delta === "string" && typeof rec.status === "string";
|
|
1619
|
+
if (!isJob) return void 0;
|
|
1620
|
+
if (typeof rec.delta === "string" && rec.delta.length > 3500) {
|
|
1621
|
+
rec.delta = `\u2026(truncated ${rec.delta.length} chars)
|
|
1622
|
+
${rec.delta.slice(-3500)}`;
|
|
1623
|
+
}
|
|
1624
|
+
if (typeof rec.progress === "string" && rec.progress.length > 800) {
|
|
1625
|
+
rec.progress = rec.progress.slice(-800);
|
|
1626
|
+
}
|
|
1627
|
+
if (typeof rec.hint === "string" && rec.hint.length > 240) {
|
|
1628
|
+
rec.hint = rec.hint.slice(0, 240);
|
|
1629
|
+
}
|
|
1630
|
+
let out = JSON.stringify(rec);
|
|
1631
|
+
if (out.length <= TOOL_RESULT_STORE_MAX_CHARS) return out;
|
|
1632
|
+
return JSON.stringify({
|
|
1633
|
+
stillRunning: rec.stillRunning,
|
|
1634
|
+
ok: rec.ok,
|
|
1635
|
+
failed: rec.failed,
|
|
1636
|
+
status: rec.status,
|
|
1637
|
+
id: rec.id,
|
|
1638
|
+
phase: rec.phase,
|
|
1639
|
+
delta: typeof rec.delta === "string" ? rec.delta.slice(-2500) : ""
|
|
1640
|
+
});
|
|
1641
|
+
} catch {
|
|
1642
|
+
return void 0;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1613
1645
|
function clipToolResultForStore(content) {
|
|
1614
1646
|
if (content == null) return void 0;
|
|
1615
1647
|
if (looksLikeCursorSdkSourceDump(content) || looksLikeMinifiedJsDump(content)) {
|
|
1616
1648
|
return `${HUGE_TOOL_RESULT_SUMMARY} (${content.length} chars omitted)`;
|
|
1617
1649
|
}
|
|
1650
|
+
const jobClipped = clipJobWaitResultJson(content);
|
|
1651
|
+
if (jobClipped) return jobClipped;
|
|
1618
1652
|
if (content.length <= TOOL_RESULT_STORE_MAX_CHARS) return content;
|
|
1619
1653
|
const head = 5e3;
|
|
1620
1654
|
const tail = 2e3;
|
|
@@ -14340,8 +14374,37 @@ function listRunningDetachedJobs(worktreePath) {
|
|
|
14340
14374
|
}
|
|
14341
14375
|
function looksLikeDeferredDonePromise(text7) {
|
|
14342
14376
|
const t = (text7 ?? "").trim();
|
|
14343
|
-
if (!t) return false;
|
|
14344
|
-
|
|
14377
|
+
if (!t || t.length > DEFERRED_DONE_MAX_CHARS) return false;
|
|
14378
|
+
const unquoted = t.replace(/`[^`]*`/g, " ").replace(/["“”][^"“”]*["“”]/g, " ").replace(/\s+/g, " ").trim();
|
|
14379
|
+
if (!unquoted) return false;
|
|
14380
|
+
return /\b(i['’]?ll|i will)\s+let you know\b/i.test(unquoted) || /\blet you know when\b/i.test(unquoted) || /\b(check back|ping me)\s+when\b/i.test(unquoted) || /\bi(?:['’]ll| will)\s+(report|update you)\s+when\b/i.test(unquoted);
|
|
14381
|
+
}
|
|
14382
|
+
function isJobContinuePrompt(text7) {
|
|
14383
|
+
const t = text7.trim();
|
|
14384
|
+
return t.startsWith("Detached job still running:") || t.startsWith("You ended the turn after promising to report later");
|
|
14385
|
+
}
|
|
14386
|
+
function collapseGhRunWatchLog(text7) {
|
|
14387
|
+
if (!text7.includes(GH_WATCH_REFRESH)) return text7;
|
|
14388
|
+
const frames = text7.split(/(?=Refreshing run status every 3 seconds\.)/);
|
|
14389
|
+
const watchFrames = frames.filter((f) => f.includes(GH_WATCH_REFRESH));
|
|
14390
|
+
if (watchFrames.length <= 1) return text7;
|
|
14391
|
+
const prefix = frames[0]?.includes(GH_WATCH_REFRESH) ? "" : frames[0] ?? "";
|
|
14392
|
+
const last = watchFrames[watchFrames.length - 1] ?? "";
|
|
14393
|
+
const omitted = watchFrames.length - 1;
|
|
14394
|
+
const note = `(${omitted} identical gh run watch refresh${omitted === 1 ? "" : "es"} omitted)
|
|
14395
|
+
`;
|
|
14396
|
+
return [prefix.trimEnd(), note, last.trimStart()].filter(Boolean).join("\n");
|
|
14397
|
+
}
|
|
14398
|
+
function capJobLogDelta(text7) {
|
|
14399
|
+
const collapsed = collapseGhRunWatchLog(text7);
|
|
14400
|
+
const lines = collapsed.split("\n");
|
|
14401
|
+
let out = lines.length > MAX_JOB_DELTA_LINES ? `\u2026(${lines.length - MAX_JOB_DELTA_LINES} earlier lines omitted)
|
|
14402
|
+
${lines.slice(-MAX_JOB_DELTA_LINES).join("\n")}` : collapsed;
|
|
14403
|
+
if (out.length > MAX_JOB_DELTA_CHARS) {
|
|
14404
|
+
out = `\u2026(truncated ${out.length} chars)
|
|
14405
|
+
${out.slice(-MAX_JOB_DELTA_CHARS)}`;
|
|
14406
|
+
}
|
|
14407
|
+
return out;
|
|
14345
14408
|
}
|
|
14346
14409
|
function formatJobStillRunningContinuePrompt(jobIds) {
|
|
14347
14410
|
const ids = jobIds.join(", ");
|
|
@@ -14404,7 +14467,7 @@ function takeDelta(logFile, cursorFile) {
|
|
|
14404
14467
|
const lines = readLogLines(logFile);
|
|
14405
14468
|
const cursor = readIntFile(cursorFile) ?? 0;
|
|
14406
14469
|
const start = Math.min(Math.max(0, cursor), lines.length);
|
|
14407
|
-
return { delta: lines.slice(start).join("\n"), nextCursor: lines.length };
|
|
14470
|
+
return { delta: capJobLogDelta(lines.slice(start).join("\n")), nextCursor: lines.length };
|
|
14408
14471
|
}
|
|
14409
14472
|
function snapshotJob(dir) {
|
|
14410
14473
|
const pid = readIntFile((0, import_node_path40.join)(dir, "pid"));
|
|
@@ -14572,7 +14635,7 @@ async function stopDetachedJob(cwd, id, opts) {
|
|
|
14572
14635
|
hint: stopped ? MCP_STOP_JOB_HINT : MCP_WAIT_JOB_STILL_RUNNING_HINT
|
|
14573
14636
|
};
|
|
14574
14637
|
}
|
|
14575
|
-
var import_node_fs40, import_node_path40, MAX_JOB_CONTINUES, MCP_WAIT_JOB_STILL_RUNNING_HINT, MCP_STOP_JOB_HINT, JOB_ID_RE;
|
|
14638
|
+
var import_node_fs40, import_node_path40, MAX_JOB_CONTINUES, MAX_JOB_DELTA_LINES, MAX_JOB_DELTA_CHARS, DEFERRED_DONE_MAX_CHARS, GH_WATCH_REFRESH, MCP_WAIT_JOB_STILL_RUNNING_HINT, MCP_STOP_JOB_HINT, JOB_ID_RE;
|
|
14576
14639
|
var init_wait_for_job = __esm({
|
|
14577
14640
|
"src/mcp/wait-for-job.ts"() {
|
|
14578
14641
|
"use strict";
|
|
@@ -14581,6 +14644,10 @@ var init_wait_for_job = __esm({
|
|
|
14581
14644
|
init_workspace_scratch();
|
|
14582
14645
|
init_wait_for_turn();
|
|
14583
14646
|
MAX_JOB_CONTINUES = 8;
|
|
14647
|
+
MAX_JOB_DELTA_LINES = 80;
|
|
14648
|
+
MAX_JOB_DELTA_CHARS = 4e3;
|
|
14649
|
+
DEFERRED_DONE_MAX_CHARS = 480;
|
|
14650
|
+
GH_WATCH_REFRESH = "Refreshing run status every 3 seconds.";
|
|
14584
14651
|
MCP_WAIT_JOB_STILL_RUNNING_HINT = "Job is still running. The type=log pane already appended this delta. Call wait_for_job again. If it is hanging, producing no useful output, or doing the wrong thing, call stop_job (same id) instead of looping forever. Do not end the turn or tell the user you will let them know later.";
|
|
14585
14652
|
MCP_STOP_JOB_HINT = "Stopped. The type=log pane is status=failed with the last delta. Do not wait again unless you start a new command.";
|
|
14586
14653
|
JOB_ID_RE = /^[a-zA-Z0-9._-]{1,64}$/;
|
|
@@ -18232,7 +18299,7 @@ function formatIssueToolsDirective(opts) {
|
|
|
18232
18299
|
];
|
|
18233
18300
|
if (opts.linear) {
|
|
18234
18301
|
lines.push(
|
|
18235
|
-
"- 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), `linear_create_issue` (pass `parent` for spin-offs; call `linear_list_teams` first). Scope errors: reconnect Linear in Settings \u2192 Issues."
|
|
18302
|
+
"- 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."
|
|
18236
18303
|
);
|
|
18237
18304
|
}
|
|
18238
18305
|
if (github) {
|
|
@@ -20097,11 +20164,13 @@ var init_orchestrator = __esm({
|
|
|
20097
20164
|
this.emit({ type: "error", threadId, message: `context compact failed: ${message}` });
|
|
20098
20165
|
thread = this.requireThread(threadId);
|
|
20099
20166
|
}
|
|
20100
|
-
const
|
|
20167
|
+
const autoContinue = isJobContinuePrompt(prompt) || prompt.trim().startsWith("The previous agent process ended before it finished.");
|
|
20168
|
+
const sentAttachments = !autoContinue && thread.attachments.length > 0 ? [...thread.attachments] : void 0;
|
|
20101
20169
|
appendMessage(threadId, {
|
|
20102
20170
|
role: "user",
|
|
20103
20171
|
text: prompt,
|
|
20104
20172
|
...sentAttachments ? { attachments: sentAttachments } : {},
|
|
20173
|
+
...autoContinue ? { origin: "continue" } : {},
|
|
20105
20174
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
20106
20175
|
});
|
|
20107
20176
|
thread = this.requireThread(threadId);
|
|
@@ -20110,7 +20179,7 @@ var init_orchestrator = __esm({
|
|
|
20110
20179
|
thread.worktreePath,
|
|
20111
20180
|
gitPrompt,
|
|
20112
20181
|
{
|
|
20113
|
-
attachments: sentAttachments ?? thread.attachments
|
|
20182
|
+
attachments: autoContinue ? [] : sentAttachments ?? thread.attachments
|
|
20114
20183
|
}
|
|
20115
20184
|
);
|
|
20116
20185
|
const slackInbound = /^(Slack DM|Slack @mention)(?:\n|$)/.test(
|
|
@@ -20163,7 +20232,7 @@ var init_orchestrator = __esm({
|
|
|
20163
20232
|
slackReplyContext,
|
|
20164
20233
|
expandedPrompt
|
|
20165
20234
|
].filter(Boolean).join("\n\n");
|
|
20166
|
-
if (thread.attachments.length > 0) {
|
|
20235
|
+
if (!autoContinue && thread.attachments.length > 0) {
|
|
20167
20236
|
updateThread(threadId, { attachments: [] });
|
|
20168
20237
|
}
|
|
20169
20238
|
const adapter = getAdapter(thread.agent);
|
|
@@ -21996,7 +22065,7 @@ var LIST_ISSUE_FIELDS = `
|
|
|
21996
22065
|
assignee { id name }
|
|
21997
22066
|
team { id key }
|
|
21998
22067
|
labels(first: 10) { nodes { name } }
|
|
21999
|
-
cycle { name number startsAt endsAt completedAt }
|
|
22068
|
+
cycle { id name number startsAt endsAt completedAt }
|
|
22000
22069
|
`;
|
|
22001
22070
|
var ISSUE_REF_FIELDS = `
|
|
22002
22071
|
id
|
|
@@ -22022,7 +22091,7 @@ var ISSUE_FIELDS = `
|
|
|
22022
22091
|
creator { id name }
|
|
22023
22092
|
team { id key name }
|
|
22024
22093
|
labels(first: 50) { nodes { name } }
|
|
22025
|
-
cycle { name number startsAt endsAt completedAt }
|
|
22094
|
+
cycle { id name number startsAt endsAt completedAt }
|
|
22026
22095
|
project { id name }
|
|
22027
22096
|
parent { ${ISSUE_REF_FIELDS} }
|
|
22028
22097
|
children(first: 25) { nodes { ${ISSUE_REF_FIELDS} } }
|
|
@@ -22107,6 +22176,19 @@ query SideboardTeams {
|
|
|
22107
22176
|
key
|
|
22108
22177
|
name
|
|
22109
22178
|
states(first: 50) { nodes { id name type } }
|
|
22179
|
+
activeCycle { id name number startsAt endsAt completedAt }
|
|
22180
|
+
}
|
|
22181
|
+
}
|
|
22182
|
+
}
|
|
22183
|
+
`;
|
|
22184
|
+
var TEAM_CYCLES_QUERY = `
|
|
22185
|
+
query SideboardTeamCycles($id: String!) {
|
|
22186
|
+
team(id: $id) {
|
|
22187
|
+
id
|
|
22188
|
+
key
|
|
22189
|
+
activeCycle { id name number startsAt endsAt completedAt }
|
|
22190
|
+
cycles(first: 40) {
|
|
22191
|
+
nodes { id name number startsAt endsAt completedAt }
|
|
22110
22192
|
}
|
|
22111
22193
|
}
|
|
22112
22194
|
}
|
|
@@ -22199,11 +22281,26 @@ function linearCycleIsActive(cycle, now = Date.now()) {
|
|
|
22199
22281
|
if (Number.isFinite(end) && now > end) return false;
|
|
22200
22282
|
return true;
|
|
22201
22283
|
}
|
|
22284
|
+
function cycleDisplayName(node) {
|
|
22285
|
+
return String(node?.name ?? (node?.number != null ? `Cycle ${node.number}` : "")).trim();
|
|
22286
|
+
}
|
|
22287
|
+
function mapNamedCycle(node) {
|
|
22288
|
+
if (!node?.id) return null;
|
|
22289
|
+
const name = cycleDisplayName(node);
|
|
22290
|
+
if (!name) return null;
|
|
22291
|
+
return {
|
|
22292
|
+
id: String(node.id),
|
|
22293
|
+
name,
|
|
22294
|
+
number: typeof node.number === "number" ? node.number : void 0,
|
|
22295
|
+
isActive: linearCycleIsActive(node)
|
|
22296
|
+
};
|
|
22297
|
+
}
|
|
22202
22298
|
function mapCycle(node) {
|
|
22203
22299
|
if (!node?.name && node?.number == null) return null;
|
|
22204
|
-
const name =
|
|
22300
|
+
const name = cycleDisplayName(node);
|
|
22205
22301
|
if (!name) return null;
|
|
22206
22302
|
return {
|
|
22303
|
+
...node.id ? { id: String(node.id) } : {},
|
|
22207
22304
|
name,
|
|
22208
22305
|
number: typeof node.number === "number" ? node.number : void 0,
|
|
22209
22306
|
isActive: linearCycleIsActive(node)
|
|
@@ -22342,6 +22439,10 @@ function toIssueInfo(issue) {
|
|
|
22342
22439
|
};
|
|
22343
22440
|
}
|
|
22344
22441
|
function mapTeam(node) {
|
|
22442
|
+
const cycles = (node.cycles?.nodes ?? []).flatMap((c) => {
|
|
22443
|
+
const cycle = mapNamedCycle(c);
|
|
22444
|
+
return cycle ? [cycle] : [];
|
|
22445
|
+
});
|
|
22345
22446
|
return {
|
|
22346
22447
|
id: String(node.id ?? ""),
|
|
22347
22448
|
key: String(node.key ?? ""),
|
|
@@ -22349,7 +22450,9 @@ function mapTeam(node) {
|
|
|
22349
22450
|
states: (node.states?.nodes ?? []).flatMap((s) => {
|
|
22350
22451
|
const state = mapState(s);
|
|
22351
22452
|
return state ? [state] : [];
|
|
22352
|
-
})
|
|
22453
|
+
}),
|
|
22454
|
+
activeCycle: mapNamedCycle(node.activeCycle),
|
|
22455
|
+
...cycles.length ? { cycles } : {}
|
|
22353
22456
|
};
|
|
22354
22457
|
}
|
|
22355
22458
|
function resolveLinearTeam(teams, team) {
|
|
@@ -22376,6 +22479,27 @@ function resolveLinearState(team, state) {
|
|
|
22376
22479
|
}
|
|
22377
22480
|
return found;
|
|
22378
22481
|
}
|
|
22482
|
+
function resolveLinearCycle(team, cycle) {
|
|
22483
|
+
if (cycle == null) return null;
|
|
22484
|
+
const s = cycle.trim();
|
|
22485
|
+
if (!s || /^(none|clear|unschedule|null)$/i.test(s)) return null;
|
|
22486
|
+
if (/^(current|active|this)$/i.test(s)) {
|
|
22487
|
+
const active = team.activeCycle ?? team.cycles?.find((c) => c.isActive);
|
|
22488
|
+
if (!active?.id) {
|
|
22489
|
+
throw new Error(`Linear team ${team.key} has no active cycle`);
|
|
22490
|
+
}
|
|
22491
|
+
return active.id;
|
|
22492
|
+
}
|
|
22493
|
+
const lower = s.toLowerCase();
|
|
22494
|
+
const asNumber = Number.parseInt(s, 10);
|
|
22495
|
+
const cycles = team.cycles ?? [];
|
|
22496
|
+
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);
|
|
22497
|
+
if (!found) {
|
|
22498
|
+
const available = cycles.map((c) => c.number != null ? `${c.name} (${c.number})` : c.name).join(", ") || "(none)";
|
|
22499
|
+
throw new Error(`Linear cycle not found on ${team.key}: ${cycle}. Available: ${available}`);
|
|
22500
|
+
}
|
|
22501
|
+
return found.id;
|
|
22502
|
+
}
|
|
22379
22503
|
function normalizePriority(priority) {
|
|
22380
22504
|
if (priority == null) return void 0;
|
|
22381
22505
|
if (!Number.isInteger(priority) || priority < 0 || priority > 4) {
|
|
@@ -22517,6 +22641,19 @@ async function listLinearTeams(opts) {
|
|
|
22517
22641
|
teams: (json.teams?.nodes ?? []).map(mapTeam)
|
|
22518
22642
|
};
|
|
22519
22643
|
}
|
|
22644
|
+
async function listLinearTeamCycles(teamId, opts) {
|
|
22645
|
+
const json = await linearGraphql(TEAM_CYCLES_QUERY, { id: teamId }, opts);
|
|
22646
|
+
if (!json.team?.id) {
|
|
22647
|
+
throw new Error(`Linear team not found: ${teamId}`);
|
|
22648
|
+
}
|
|
22649
|
+
const mapped = mapTeam(json.team);
|
|
22650
|
+
return {
|
|
22651
|
+
id: mapped.id,
|
|
22652
|
+
key: mapped.key,
|
|
22653
|
+
activeCycle: mapped.activeCycle,
|
|
22654
|
+
cycles: mapped.cycles ?? []
|
|
22655
|
+
};
|
|
22656
|
+
}
|
|
22520
22657
|
async function getLinearIssue(id, opts) {
|
|
22521
22658
|
const issueId = id.trim();
|
|
22522
22659
|
if (!issueId) throw new Error("Linear issue id is required (uuid or ENG-123)");
|
|
@@ -22572,16 +22709,28 @@ async function updateLinearIssue(input, opts) {
|
|
|
22572
22709
|
if (input.description !== void 0) {
|
|
22573
22710
|
mutationInput.description = input.description;
|
|
22574
22711
|
}
|
|
22712
|
+
const needsTeam = Boolean(input.state?.trim()) || input.cycle !== void 0;
|
|
22713
|
+
const existing = needsTeam ? await getLinearIssue(issueId, opts) : void 0;
|
|
22575
22714
|
if (input.state?.trim()) {
|
|
22576
|
-
const existing = await getLinearIssue(issueId, opts);
|
|
22577
22715
|
const { teams } = await listLinearTeams(opts);
|
|
22578
|
-
const teamRef = existing
|
|
22716
|
+
const teamRef = existing?.team?.key || existing?.team?.id;
|
|
22579
22717
|
const team = teamRef ? resolveLinearTeam(teams, teamRef) : void 0;
|
|
22580
22718
|
if (!team?.states.length) {
|
|
22581
|
-
throw new Error(`Linear issue ${existing
|
|
22719
|
+
throw new Error(`Linear issue ${existing?.identifier ?? issueId} has no workflow states to resolve "${input.state}"`);
|
|
22582
22720
|
}
|
|
22583
22721
|
mutationInput.stateId = resolveLinearState(team, input.state).id;
|
|
22584
22722
|
}
|
|
22723
|
+
if (input.cycle !== void 0) {
|
|
22724
|
+
const teamId = existing?.team?.id;
|
|
22725
|
+
if (!teamId) {
|
|
22726
|
+
throw new Error(`Linear issue ${existing?.identifier ?? issueId} has no team to resolve a cycle`);
|
|
22727
|
+
}
|
|
22728
|
+
const cycles = await listLinearTeamCycles(teamId, opts);
|
|
22729
|
+
mutationInput.cycleId = resolveLinearCycle(
|
|
22730
|
+
{ ...cycles, key: cycles.key || existing?.team?.key || teamId },
|
|
22731
|
+
input.cycle
|
|
22732
|
+
);
|
|
22733
|
+
}
|
|
22585
22734
|
if (input.assignee !== void 0) {
|
|
22586
22735
|
const assignee = input.assignee?.trim() || null;
|
|
22587
22736
|
if (assignee === "me") {
|
|
@@ -22595,7 +22744,7 @@ async function updateLinearIssue(input, opts) {
|
|
|
22595
22744
|
mutationInput.priority = normalizePriority(input.priority);
|
|
22596
22745
|
}
|
|
22597
22746
|
if (Object.keys(mutationInput).length === 0) {
|
|
22598
|
-
throw new Error("linear_update_issue needs at least one of title, description, state, assignee, priority");
|
|
22747
|
+
throw new Error("linear_update_issue needs at least one of title, description, state, assignee, priority, cycle");
|
|
22599
22748
|
}
|
|
22600
22749
|
const json = await linearGraphql(ISSUE_UPDATE, { id: issueId, input: mutationInput }, opts);
|
|
22601
22750
|
if (!json.issueUpdate?.success || !json.issueUpdate.issue) {
|
|
@@ -24063,7 +24212,7 @@ var prioritySchema = import_zod6.z.number().int().min(0).max(4).optional().descr
|
|
|
24063
24212
|
function registerLinearTools(server) {
|
|
24064
24213
|
server.tool(
|
|
24065
24214
|
"linear_list_teams",
|
|
24066
|
-
|
|
24215
|
+
'List Linear teams (id, key, name), workflow states, and the active cycle. Use team key on create; state name or type on create/update; cycle name/"current"/"none" on update.',
|
|
24067
24216
|
{},
|
|
24068
24217
|
async () => {
|
|
24069
24218
|
try {
|
|
@@ -24150,14 +24299,15 @@ function registerLinearTools(server) {
|
|
|
24150
24299
|
);
|
|
24151
24300
|
server.tool(
|
|
24152
24301
|
"linear_update_issue",
|
|
24153
|
-
|
|
24302
|
+
'Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, priority, and/or cycle. Cycle is name, number, "current"/"active", or "none" to unschedule. 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.',
|
|
24154
24303
|
{
|
|
24155
24304
|
id: import_zod6.z.string(),
|
|
24156
24305
|
title: import_zod6.z.string().optional(),
|
|
24157
24306
|
description: import_zod6.z.string().optional(),
|
|
24158
24307
|
state: import_zod6.z.string().optional(),
|
|
24159
24308
|
assignee: import_zod6.z.string().nullable().optional(),
|
|
24160
|
-
priority: prioritySchema
|
|
24309
|
+
priority: prioritySchema,
|
|
24310
|
+
cycle: import_zod6.z.string().nullable().optional().describe('Cycle name, number, "current"/"active", or "none" to unschedule.')
|
|
24161
24311
|
},
|
|
24162
24312
|
async (args) => {
|
|
24163
24313
|
try {
|
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
syncBoardPins,
|
|
38
38
|
updateSchedule,
|
|
39
39
|
waitForDetachedJob
|
|
40
|
-
} from "../chunk-
|
|
40
|
+
} from "../chunk-LE5OZCKB.js";
|
|
41
41
|
import {
|
|
42
42
|
applySystemCaEnv,
|
|
43
43
|
isCursorApiKeyConfigured,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
materializeSystemCaBundle,
|
|
47
47
|
sideboardMcpProfile,
|
|
48
48
|
trustSystemCertificates
|
|
49
|
-
} from "../chunk-
|
|
49
|
+
} from "../chunk-QU663NPO.js";
|
|
50
50
|
import "../chunk-HVIEYTUK.js";
|
|
51
51
|
import "../chunk-Y4MIXGPA.js";
|
|
52
52
|
import {
|
|
@@ -531,7 +531,7 @@ var LIST_ISSUE_FIELDS = `
|
|
|
531
531
|
assignee { id name }
|
|
532
532
|
team { id key }
|
|
533
533
|
labels(first: 10) { nodes { name } }
|
|
534
|
-
cycle { name number startsAt endsAt completedAt }
|
|
534
|
+
cycle { id name number startsAt endsAt completedAt }
|
|
535
535
|
`;
|
|
536
536
|
var ISSUE_REF_FIELDS = `
|
|
537
537
|
id
|
|
@@ -557,7 +557,7 @@ var ISSUE_FIELDS = `
|
|
|
557
557
|
creator { id name }
|
|
558
558
|
team { id key name }
|
|
559
559
|
labels(first: 50) { nodes { name } }
|
|
560
|
-
cycle { name number startsAt endsAt completedAt }
|
|
560
|
+
cycle { id name number startsAt endsAt completedAt }
|
|
561
561
|
project { id name }
|
|
562
562
|
parent { ${ISSUE_REF_FIELDS} }
|
|
563
563
|
children(first: 25) { nodes { ${ISSUE_REF_FIELDS} } }
|
|
@@ -642,6 +642,19 @@ query SideboardTeams {
|
|
|
642
642
|
key
|
|
643
643
|
name
|
|
644
644
|
states(first: 50) { nodes { id name type } }
|
|
645
|
+
activeCycle { id name number startsAt endsAt completedAt }
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
`;
|
|
650
|
+
var TEAM_CYCLES_QUERY = `
|
|
651
|
+
query SideboardTeamCycles($id: String!) {
|
|
652
|
+
team(id: $id) {
|
|
653
|
+
id
|
|
654
|
+
key
|
|
655
|
+
activeCycle { id name number startsAt endsAt completedAt }
|
|
656
|
+
cycles(first: 40) {
|
|
657
|
+
nodes { id name number startsAt endsAt completedAt }
|
|
645
658
|
}
|
|
646
659
|
}
|
|
647
660
|
}
|
|
@@ -734,11 +747,26 @@ function linearCycleIsActive(cycle, now = Date.now()) {
|
|
|
734
747
|
if (Number.isFinite(end) && now > end) return false;
|
|
735
748
|
return true;
|
|
736
749
|
}
|
|
750
|
+
function cycleDisplayName(node) {
|
|
751
|
+
return String(node?.name ?? (node?.number != null ? `Cycle ${node.number}` : "")).trim();
|
|
752
|
+
}
|
|
753
|
+
function mapNamedCycle(node) {
|
|
754
|
+
if (!node?.id) return null;
|
|
755
|
+
const name = cycleDisplayName(node);
|
|
756
|
+
if (!name) return null;
|
|
757
|
+
return {
|
|
758
|
+
id: String(node.id),
|
|
759
|
+
name,
|
|
760
|
+
number: typeof node.number === "number" ? node.number : void 0,
|
|
761
|
+
isActive: linearCycleIsActive(node)
|
|
762
|
+
};
|
|
763
|
+
}
|
|
737
764
|
function mapCycle(node) {
|
|
738
765
|
if (!node?.name && node?.number == null) return null;
|
|
739
|
-
const name =
|
|
766
|
+
const name = cycleDisplayName(node);
|
|
740
767
|
if (!name) return null;
|
|
741
768
|
return {
|
|
769
|
+
...node.id ? { id: String(node.id) } : {},
|
|
742
770
|
name,
|
|
743
771
|
number: typeof node.number === "number" ? node.number : void 0,
|
|
744
772
|
isActive: linearCycleIsActive(node)
|
|
@@ -877,6 +905,10 @@ function toIssueInfo(issue) {
|
|
|
877
905
|
};
|
|
878
906
|
}
|
|
879
907
|
function mapTeam(node) {
|
|
908
|
+
const cycles = (node.cycles?.nodes ?? []).flatMap((c) => {
|
|
909
|
+
const cycle = mapNamedCycle(c);
|
|
910
|
+
return cycle ? [cycle] : [];
|
|
911
|
+
});
|
|
880
912
|
return {
|
|
881
913
|
id: String(node.id ?? ""),
|
|
882
914
|
key: String(node.key ?? ""),
|
|
@@ -884,7 +916,9 @@ function mapTeam(node) {
|
|
|
884
916
|
states: (node.states?.nodes ?? []).flatMap((s) => {
|
|
885
917
|
const state = mapState(s);
|
|
886
918
|
return state ? [state] : [];
|
|
887
|
-
})
|
|
919
|
+
}),
|
|
920
|
+
activeCycle: mapNamedCycle(node.activeCycle),
|
|
921
|
+
...cycles.length ? { cycles } : {}
|
|
888
922
|
};
|
|
889
923
|
}
|
|
890
924
|
function resolveLinearTeam(teams, team) {
|
|
@@ -911,6 +945,27 @@ function resolveLinearState(team, state) {
|
|
|
911
945
|
}
|
|
912
946
|
return found;
|
|
913
947
|
}
|
|
948
|
+
function resolveLinearCycle(team, cycle) {
|
|
949
|
+
if (cycle == null) return null;
|
|
950
|
+
const s = cycle.trim();
|
|
951
|
+
if (!s || /^(none|clear|unschedule|null)$/i.test(s)) return null;
|
|
952
|
+
if (/^(current|active|this)$/i.test(s)) {
|
|
953
|
+
const active = team.activeCycle ?? team.cycles?.find((c) => c.isActive);
|
|
954
|
+
if (!active?.id) {
|
|
955
|
+
throw new Error(`Linear team ${team.key} has no active cycle`);
|
|
956
|
+
}
|
|
957
|
+
return active.id;
|
|
958
|
+
}
|
|
959
|
+
const lower = s.toLowerCase();
|
|
960
|
+
const asNumber = Number.parseInt(s, 10);
|
|
961
|
+
const cycles = team.cycles ?? [];
|
|
962
|
+
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);
|
|
963
|
+
if (!found) {
|
|
964
|
+
const available = cycles.map((c) => c.number != null ? `${c.name} (${c.number})` : c.name).join(", ") || "(none)";
|
|
965
|
+
throw new Error(`Linear cycle not found on ${team.key}: ${cycle}. Available: ${available}`);
|
|
966
|
+
}
|
|
967
|
+
return found.id;
|
|
968
|
+
}
|
|
914
969
|
function normalizePriority(priority) {
|
|
915
970
|
if (priority == null) return void 0;
|
|
916
971
|
if (!Number.isInteger(priority) || priority < 0 || priority > 4) {
|
|
@@ -1052,6 +1107,19 @@ async function listLinearTeams(opts) {
|
|
|
1052
1107
|
teams: (json.teams?.nodes ?? []).map(mapTeam)
|
|
1053
1108
|
};
|
|
1054
1109
|
}
|
|
1110
|
+
async function listLinearTeamCycles(teamId, opts) {
|
|
1111
|
+
const json = await linearGraphql(TEAM_CYCLES_QUERY, { id: teamId }, opts);
|
|
1112
|
+
if (!json.team?.id) {
|
|
1113
|
+
throw new Error(`Linear team not found: ${teamId}`);
|
|
1114
|
+
}
|
|
1115
|
+
const mapped = mapTeam(json.team);
|
|
1116
|
+
return {
|
|
1117
|
+
id: mapped.id,
|
|
1118
|
+
key: mapped.key,
|
|
1119
|
+
activeCycle: mapped.activeCycle,
|
|
1120
|
+
cycles: mapped.cycles ?? []
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1055
1123
|
async function getLinearIssue(id, opts) {
|
|
1056
1124
|
const issueId = id.trim();
|
|
1057
1125
|
if (!issueId) throw new Error("Linear issue id is required (uuid or ENG-123)");
|
|
@@ -1107,16 +1175,28 @@ async function updateLinearIssue(input, opts) {
|
|
|
1107
1175
|
if (input.description !== void 0) {
|
|
1108
1176
|
mutationInput.description = input.description;
|
|
1109
1177
|
}
|
|
1178
|
+
const needsTeam = Boolean(input.state?.trim()) || input.cycle !== void 0;
|
|
1179
|
+
const existing = needsTeam ? await getLinearIssue(issueId, opts) : void 0;
|
|
1110
1180
|
if (input.state?.trim()) {
|
|
1111
|
-
const existing = await getLinearIssue(issueId, opts);
|
|
1112
1181
|
const { teams } = await listLinearTeams(opts);
|
|
1113
|
-
const teamRef = existing
|
|
1182
|
+
const teamRef = existing?.team?.key || existing?.team?.id;
|
|
1114
1183
|
const team = teamRef ? resolveLinearTeam(teams, teamRef) : void 0;
|
|
1115
1184
|
if (!team?.states.length) {
|
|
1116
|
-
throw new Error(`Linear issue ${existing
|
|
1185
|
+
throw new Error(`Linear issue ${existing?.identifier ?? issueId} has no workflow states to resolve "${input.state}"`);
|
|
1117
1186
|
}
|
|
1118
1187
|
mutationInput.stateId = resolveLinearState(team, input.state).id;
|
|
1119
1188
|
}
|
|
1189
|
+
if (input.cycle !== void 0) {
|
|
1190
|
+
const teamId = existing?.team?.id;
|
|
1191
|
+
if (!teamId) {
|
|
1192
|
+
throw new Error(`Linear issue ${existing?.identifier ?? issueId} has no team to resolve a cycle`);
|
|
1193
|
+
}
|
|
1194
|
+
const cycles = await listLinearTeamCycles(teamId, opts);
|
|
1195
|
+
mutationInput.cycleId = resolveLinearCycle(
|
|
1196
|
+
{ ...cycles, key: cycles.key || existing?.team?.key || teamId },
|
|
1197
|
+
input.cycle
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1120
1200
|
if (input.assignee !== void 0) {
|
|
1121
1201
|
const assignee = input.assignee?.trim() || null;
|
|
1122
1202
|
if (assignee === "me") {
|
|
@@ -1130,7 +1210,7 @@ async function updateLinearIssue(input, opts) {
|
|
|
1130
1210
|
mutationInput.priority = normalizePriority(input.priority);
|
|
1131
1211
|
}
|
|
1132
1212
|
if (Object.keys(mutationInput).length === 0) {
|
|
1133
|
-
throw new Error("linear_update_issue needs at least one of title, description, state, assignee, priority");
|
|
1213
|
+
throw new Error("linear_update_issue needs at least one of title, description, state, assignee, priority, cycle");
|
|
1134
1214
|
}
|
|
1135
1215
|
const json = await linearGraphql(ISSUE_UPDATE, { id: issueId, input: mutationInput }, opts);
|
|
1136
1216
|
if (!json.issueUpdate?.success || !json.issueUpdate.issue) {
|
|
@@ -2573,7 +2653,7 @@ var prioritySchema = z6.number().int().min(0).max(4).optional().describe("0 none
|
|
|
2573
2653
|
function registerLinearTools(server) {
|
|
2574
2654
|
server.tool(
|
|
2575
2655
|
"linear_list_teams",
|
|
2576
|
-
|
|
2656
|
+
'List Linear teams (id, key, name), workflow states, and the active cycle. Use team key on create; state name or type on create/update; cycle name/"current"/"none" on update.',
|
|
2577
2657
|
{},
|
|
2578
2658
|
async () => {
|
|
2579
2659
|
try {
|
|
@@ -2660,14 +2740,15 @@ function registerLinearTools(server) {
|
|
|
2660
2740
|
);
|
|
2661
2741
|
server.tool(
|
|
2662
2742
|
"linear_update_issue",
|
|
2663
|
-
|
|
2743
|
+
'Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, priority, and/or cycle. Cycle is name, number, "current"/"active", or "none" to unschedule. 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.',
|
|
2664
2744
|
{
|
|
2665
2745
|
id: z6.string(),
|
|
2666
2746
|
title: z6.string().optional(),
|
|
2667
2747
|
description: z6.string().optional(),
|
|
2668
2748
|
state: z6.string().optional(),
|
|
2669
2749
|
assignee: z6.string().nullable().optional(),
|
|
2670
|
-
priority: prioritySchema
|
|
2750
|
+
priority: prioritySchema,
|
|
2751
|
+
cycle: z6.string().nullable().optional().describe('Cycle name, number, "current"/"active", or "none" to unschedule.')
|
|
2671
2752
|
},
|
|
2672
2753
|
async (args) => {
|
|
2673
2754
|
try {
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
resolveSendFollowUp,
|
|
7
7
|
startOrchestration,
|
|
8
8
|
waitForPidExit
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-U6FCBOS7.js";
|
|
10
10
|
import "./chunk-DJU54RYW.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-ISOCLQEX.js";
|
|
12
12
|
import "./chunk-VIODP5PT.js";
|
|
13
13
|
import "./chunk-WXKXSZN4.js";
|
|
14
14
|
import "./chunk-6K64FJDD.js";
|
|
@@ -16,7 +16,7 @@ import "./chunk-IEEAVWAP.js";
|
|
|
16
16
|
import "./chunk-AN3THU7F.js";
|
|
17
17
|
import "./chunk-N7WZ6NIN.js";
|
|
18
18
|
import "./chunk-AXQ4ZWHK.js";
|
|
19
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-ZSFQLR7Y.js";
|
|
20
20
|
import "./chunk-J7LQVLFQ.js";
|
|
21
21
|
import "./chunk-4TR3HZFT.js";
|
|
22
22
|
import "./chunk-AO35MGOE.js";
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
resolveSendFollowUp,
|
|
9
9
|
startOrchestration,
|
|
10
10
|
waitForPidExit
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-LE5OZCKB.js";
|
|
12
|
+
import "./chunk-QU663NPO.js";
|
|
13
13
|
import "./chunk-HVIEYTUK.js";
|
|
14
14
|
import "./chunk-Y4MIXGPA.js";
|
|
15
15
|
import "./chunk-VKQGAWHY.js";
|