@sideboard-ai/core 0.1.190 → 0.1.192
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-5OBZETUV.js → agents-T5VBB6X3.js} +2 -2
- package/dist/{agents-DTPVTDB5.js → agents-TXGQIK2E.js} +2 -2
- package/dist/{chunk-TEJU6UJY.js → chunk-4M2AK3O5.js} +126 -20
- package/dist/{chunk-XXEIF2IL.js → chunk-7NSXGL6L.js} +3 -3
- package/dist/{chunk-IAOXW6RI.js → chunk-FZ3G5TXM.js} +23 -16
- package/dist/{chunk-Z6TW3A5P.js → chunk-NF6ENGHS.js} +24 -17
- package/dist/{chunk-OLDZXO53.js → chunk-TWSCTJCR.js} +2 -2
- package/dist/{chunk-KEVY4GEK.js → chunk-UBQIELFS.js} +123 -20
- package/dist/{chunk-VIODP5PT.js → chunk-VV7UT3E2.js} +3 -3
- package/dist/{chunk-GBWKMWUQ.js → chunk-WHD4JNTN.js} +2 -2
- package/dist/{coordinator-prompt-LWKOL3AZ.js → coordinator-prompt-PNUDN4DW.js} +1 -1
- package/dist/{coordinator-prompt-MSC44OOM.js → coordinator-prompt-S4HHFSLI.js} +1 -1
- package/dist/{global-workspace-6G5JJH2E.js → global-workspace-2BVCJUAL.js} +1 -1
- package/dist/{global-workspace-XATJTV3N.js → global-workspace-JTBBH2K5.js} +1 -1
- package/dist/index.cjs +565 -57
- package/dist/index.d.cts +96 -6
- package/dist/index.d.ts +96 -6
- package/dist/index.js +422 -30
- package/dist/mcp/run-stdio.cjs +543 -57
- package/dist/mcp/run-stdio.js +407 -30
- package/dist/{orchestrator-BHJELEFM.js → orchestrator-4G76BOHH.js} +5 -3
- package/dist/{orchestrator-HWNGCDGW.js → orchestrator-LWEZ74RK.js} +5 -3
- package/dist/{workspaces-LMFCRXTT.js → workspaces-OY7D5RPU.js} +1 -1
- package/dist/{workspaces-FWKPWSXV.js → workspaces-ZYPME3YD.js} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -197,6 +197,7 @@ import {
|
|
|
197
197
|
resolveCreateFirstPrompt,
|
|
198
198
|
resolveDetachedJobScript,
|
|
199
199
|
resolveFilesToCopy,
|
|
200
|
+
resolveOrchChildFollowUp,
|
|
200
201
|
resolveReviewGuidelines,
|
|
201
202
|
resolveScheduleThreadId,
|
|
202
203
|
resolveSendFollowUp,
|
|
@@ -238,7 +239,7 @@ import {
|
|
|
238
239
|
worktreeCleanupSettings,
|
|
239
240
|
wrapReviewSkillMarkdown,
|
|
240
241
|
writeWorktreeFile
|
|
241
|
-
} from "./chunk-
|
|
242
|
+
} from "./chunk-NF6ENGHS.js";
|
|
242
243
|
import {
|
|
243
244
|
LEGACY_PLAN_FILE_REL,
|
|
244
245
|
PLAN_FILE_NAME,
|
|
@@ -344,7 +345,7 @@ import {
|
|
|
344
345
|
withEventParentId,
|
|
345
346
|
withEventsParentId,
|
|
346
347
|
writeInjectedMcpConfig
|
|
347
|
-
} from "./chunk-
|
|
348
|
+
} from "./chunk-TWSCTJCR.js";
|
|
348
349
|
import {
|
|
349
350
|
CLOUD_COORDINATOR_BUSY_REPLY,
|
|
350
351
|
CLOUD_COORDINATOR_STOPPED_REPLY,
|
|
@@ -387,7 +388,7 @@ import {
|
|
|
387
388
|
stageBuffersAsAttachments,
|
|
388
389
|
syncWorkspacesFromThreads,
|
|
389
390
|
takenTeamSlugsForOrchestration
|
|
390
|
-
} from "./chunk-
|
|
391
|
+
} from "./chunk-VV7UT3E2.js";
|
|
391
392
|
import {
|
|
392
393
|
FAMOUS_SOCCER_TEAMS,
|
|
393
394
|
GITHUB_PR_BODY_MAX_CHARS,
|
|
@@ -514,9 +515,13 @@ import {
|
|
|
514
515
|
} from "./chunk-AN3THU7F.js";
|
|
515
516
|
import {
|
|
516
517
|
ABLETIME_MCP_PATH,
|
|
518
|
+
ABLETIME_MCP_PM_PATH,
|
|
519
|
+
ABLETIME_PM_TOOL_NAMES,
|
|
517
520
|
DEFAULT_ABLETIME_HOST,
|
|
521
|
+
ableTimeDependencyTarget,
|
|
518
522
|
abletimeMcpRequest,
|
|
519
523
|
abletimeMcpUrl,
|
|
524
|
+
abletimeToolUsesPm,
|
|
520
525
|
callAbleTimeTool,
|
|
521
526
|
commentAbleTimeTask,
|
|
522
527
|
createAbleTimeTask,
|
|
@@ -535,7 +540,7 @@ import {
|
|
|
535
540
|
toAbleTimeIssueInfo,
|
|
536
541
|
updateAbleTimeTask,
|
|
537
542
|
verifyAbleTimeConnection
|
|
538
|
-
} from "./chunk-
|
|
543
|
+
} from "./chunk-4M2AK3O5.js";
|
|
539
544
|
import {
|
|
540
545
|
formatFetchError,
|
|
541
546
|
httpFetch,
|
|
@@ -1159,12 +1164,14 @@ var ISSUE_FIELDS = `
|
|
|
1159
1164
|
children(first: 25) { nodes { ${ISSUE_REF_FIELDS} } }
|
|
1160
1165
|
relations(first: 25) {
|
|
1161
1166
|
nodes {
|
|
1167
|
+
id
|
|
1162
1168
|
type
|
|
1163
1169
|
relatedIssue { ${ISSUE_REF_FIELDS} }
|
|
1164
1170
|
}
|
|
1165
1171
|
}
|
|
1166
1172
|
inverseRelations(first: 25) {
|
|
1167
1173
|
nodes {
|
|
1174
|
+
id
|
|
1168
1175
|
type
|
|
1169
1176
|
issue { ${ISSUE_REF_FIELDS} }
|
|
1170
1177
|
}
|
|
@@ -1284,6 +1291,45 @@ mutation SideboardCommentCreate($input: CommentCreateInput!) {
|
|
|
1284
1291
|
}
|
|
1285
1292
|
}
|
|
1286
1293
|
`;
|
|
1294
|
+
var LABELS_QUERY = `
|
|
1295
|
+
query SideboardLabels($first: Int!) {
|
|
1296
|
+
issueLabels(first: $first) {
|
|
1297
|
+
nodes { id name }
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
`;
|
|
1301
|
+
var TEAM_LABELS_QUERY = `
|
|
1302
|
+
query SideboardTeamLabels($id: String!, $first: Int!, $after: String) {
|
|
1303
|
+
team(id: $id) {
|
|
1304
|
+
labels(first: $first, after: $after) {
|
|
1305
|
+
nodes { id name }
|
|
1306
|
+
pageInfo { hasNextPage endCursor }
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
`;
|
|
1311
|
+
var PROJECTS_QUERY = `
|
|
1312
|
+
query SideboardProjects($first: Int!, $filter: ProjectFilter) {
|
|
1313
|
+
projects(first: $first, filter: $filter) {
|
|
1314
|
+
nodes { id name slugId }
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
`;
|
|
1318
|
+
var ISSUE_RELATION_CREATE = `
|
|
1319
|
+
mutation SideboardIssueRelationCreate($input: IssueRelationCreateInput!) {
|
|
1320
|
+
issueRelationCreate(input: $input) {
|
|
1321
|
+
success
|
|
1322
|
+
issueRelation { id type }
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
`;
|
|
1326
|
+
var ISSUE_RELATION_DELETE = `
|
|
1327
|
+
mutation SideboardIssueRelationDelete($id: String!) {
|
|
1328
|
+
issueRelationDelete(id: $id) {
|
|
1329
|
+
success
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
`;
|
|
1287
1333
|
function rewriteLinearError(message) {
|
|
1288
1334
|
if (/scope|permission|not (authorized|allowed)|unauthorized|insufficient/i.test(message)) {
|
|
1289
1335
|
return `${message} \u2014 Disconnect and Connect Linear in Account settings to grant write access.`;
|
|
@@ -1403,12 +1449,18 @@ function mapRelations(node) {
|
|
|
1403
1449
|
for (const rel of node.relations?.nodes ?? []) {
|
|
1404
1450
|
const issue = mapIssueRef(rel.relatedIssue);
|
|
1405
1451
|
if (!issue) continue;
|
|
1406
|
-
|
|
1452
|
+
const id = rel.id?.trim();
|
|
1453
|
+
out.push({ type: String(rel.type ?? "related"), issue, ...id ? { id } : {} });
|
|
1407
1454
|
}
|
|
1408
1455
|
for (const rel of node.inverseRelations?.nodes ?? []) {
|
|
1409
1456
|
const issue = mapIssueRef(rel.issue);
|
|
1410
1457
|
if (!issue) continue;
|
|
1411
|
-
|
|
1458
|
+
const id = rel.id?.trim();
|
|
1459
|
+
out.push({
|
|
1460
|
+
type: flipLinearRelationType(String(rel.type ?? "related")),
|
|
1461
|
+
issue,
|
|
1462
|
+
...id ? { id } : {}
|
|
1463
|
+
});
|
|
1412
1464
|
}
|
|
1413
1465
|
return out;
|
|
1414
1466
|
}
|
|
@@ -1569,6 +1621,171 @@ function resolveLinearCycle(team, cycle) {
|
|
|
1569
1621
|
}
|
|
1570
1622
|
return found.id;
|
|
1571
1623
|
}
|
|
1624
|
+
var LINEAR_NONE_RE = /^(none|clear|unschedule|null)$/i;
|
|
1625
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1626
|
+
function isLinearNoneToken(value) {
|
|
1627
|
+
if (value == null) return true;
|
|
1628
|
+
const s = value.trim();
|
|
1629
|
+
return !s || LINEAR_NONE_RE.test(s);
|
|
1630
|
+
}
|
|
1631
|
+
function resolveLinearLabel(labels, name) {
|
|
1632
|
+
const s = name.trim();
|
|
1633
|
+
if (!s) throw new Error("Linear label is empty");
|
|
1634
|
+
const lower = s.toLowerCase();
|
|
1635
|
+
const found = labels.find((x) => x.id === s) || labels.find((x) => x.name.toLowerCase() === lower);
|
|
1636
|
+
if (!found) {
|
|
1637
|
+
const available = labels.map((x) => x.name).filter(Boolean).slice(0, 20).join(", ") || "(none)";
|
|
1638
|
+
throw new Error(`Linear label not found: ${name}. Available: ${available}`);
|
|
1639
|
+
}
|
|
1640
|
+
return found;
|
|
1641
|
+
}
|
|
1642
|
+
function resolveLinearProject(projects, project) {
|
|
1643
|
+
if (isLinearNoneToken(project)) return null;
|
|
1644
|
+
const s = (project ?? "").trim();
|
|
1645
|
+
const lower = s.toLowerCase();
|
|
1646
|
+
const found = projects.find((x) => x.id === s) || projects.find((x) => x.name.toLowerCase() === lower) || projects.find((x) => x.slugId?.toLowerCase() === lower);
|
|
1647
|
+
if (!found) {
|
|
1648
|
+
const available = projects.map((x) => x.name).filter(Boolean).slice(0, 20).join(", ") || "(none)";
|
|
1649
|
+
throw new Error(`Linear project not found: ${project}. Available: ${available}`);
|
|
1650
|
+
}
|
|
1651
|
+
return found.id;
|
|
1652
|
+
}
|
|
1653
|
+
function normalizeLinearRelationType(type) {
|
|
1654
|
+
const s = type.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
1655
|
+
if (s === "blocks" || s === "block") return "blocks";
|
|
1656
|
+
if (s === "blockedby" || s === "blocked") return "blockedBy";
|
|
1657
|
+
if (s === "related" || s === "relates" || s === "relatesto") return "related";
|
|
1658
|
+
if (s === "duplicate" || s === "dup") return "duplicate";
|
|
1659
|
+
if (s === "duplicateof" || s === "original") return "duplicateOf";
|
|
1660
|
+
if (s === "similar") return "similar";
|
|
1661
|
+
throw new Error(
|
|
1662
|
+
`Linear relation type must be blocks, blockedBy, related, duplicate, or duplicateOf (got ${type})`
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
function linearRelationCreateInput(thisIssueId, type, relatedIssueId) {
|
|
1666
|
+
const view = normalizeLinearRelationType(type);
|
|
1667
|
+
if (view === "blockedBy") {
|
|
1668
|
+
return { issueId: relatedIssueId, relatedIssueId: thisIssueId, type: "blocks" };
|
|
1669
|
+
}
|
|
1670
|
+
if (view === "duplicateOf") {
|
|
1671
|
+
return { issueId: relatedIssueId, relatedIssueId: thisIssueId, type: "duplicate" };
|
|
1672
|
+
}
|
|
1673
|
+
return { issueId: thisIssueId, relatedIssueId, type: view };
|
|
1674
|
+
}
|
|
1675
|
+
function relationMatches(rel, type, issueRef) {
|
|
1676
|
+
if (rel.type.toLowerCase() !== type.toLowerCase()) return false;
|
|
1677
|
+
const ref = issueRef.trim().toLowerCase();
|
|
1678
|
+
return rel.issue.id.toLowerCase() === ref || rel.issue.identifier.toLowerCase() === ref;
|
|
1679
|
+
}
|
|
1680
|
+
function formatRelationList(relations) {
|
|
1681
|
+
if (!relations.length) return "(none)";
|
|
1682
|
+
return relations.map((r) => `${r.type} ${r.issue.identifier || r.issue.id}`).join(", ");
|
|
1683
|
+
}
|
|
1684
|
+
function mapLabelNodes(nodes) {
|
|
1685
|
+
return (nodes ?? []).flatMap((node) => {
|
|
1686
|
+
const id = String(node.id ?? "").trim();
|
|
1687
|
+
const name = String(node.name ?? "").trim();
|
|
1688
|
+
if (!id) return [];
|
|
1689
|
+
return [{ id, name }];
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
async function listLinearWorkspaceLabels(opts) {
|
|
1693
|
+
const json = await linearGraphql(LABELS_QUERY, { first: 250 }, opts);
|
|
1694
|
+
return mapLabelNodes(json.issueLabels?.nodes);
|
|
1695
|
+
}
|
|
1696
|
+
async function listLinearLabels(opts) {
|
|
1697
|
+
const teamId = opts?.teamId?.trim();
|
|
1698
|
+
if (teamId) {
|
|
1699
|
+
const out = [];
|
|
1700
|
+
let after;
|
|
1701
|
+
for (let page = 0; page < 4; page++) {
|
|
1702
|
+
const json = await linearGraphql(
|
|
1703
|
+
TEAM_LABELS_QUERY,
|
|
1704
|
+
after ? { id: teamId, first: 250, after } : { id: teamId, first: 250 },
|
|
1705
|
+
opts
|
|
1706
|
+
);
|
|
1707
|
+
const conn = json.team?.labels;
|
|
1708
|
+
out.push(...mapLabelNodes(conn?.nodes));
|
|
1709
|
+
if (!conn?.pageInfo?.hasNextPage || !conn.pageInfo.endCursor) break;
|
|
1710
|
+
after = conn.pageInfo.endCursor;
|
|
1711
|
+
}
|
|
1712
|
+
if (out.length) return out;
|
|
1713
|
+
}
|
|
1714
|
+
return listLinearWorkspaceLabels(opts);
|
|
1715
|
+
}
|
|
1716
|
+
async function listLinearProjects(filter, opts) {
|
|
1717
|
+
const json = await linearGraphql(PROJECTS_QUERY, { first: 50, filter: filter ?? {} }, opts);
|
|
1718
|
+
return (json.projects?.nodes ?? []).flatMap((node) => {
|
|
1719
|
+
const id = String(node.id ?? "").trim();
|
|
1720
|
+
if (!id) return [];
|
|
1721
|
+
return [
|
|
1722
|
+
{
|
|
1723
|
+
id,
|
|
1724
|
+
name: String(node.name ?? ""),
|
|
1725
|
+
...node.slugId ? { slugId: String(node.slugId) } : {}
|
|
1726
|
+
}
|
|
1727
|
+
];
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
async function resolveLinearProjectId(project, opts) {
|
|
1731
|
+
if (isLinearNoneToken(project)) return null;
|
|
1732
|
+
const s = (project ?? "").trim();
|
|
1733
|
+
const filter = UUID_RE.test(s) ? { id: { eq: s } } : { name: { eqIgnoreCase: s } };
|
|
1734
|
+
const matched = await listLinearProjects(filter, opts);
|
|
1735
|
+
try {
|
|
1736
|
+
return resolveLinearProject(matched, s);
|
|
1737
|
+
} catch {
|
|
1738
|
+
if (UUID_RE.test(s)) return s;
|
|
1739
|
+
const listed = await listLinearProjects(void 0, opts);
|
|
1740
|
+
return resolveLinearProject(listed, s);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
async function resolveLinearLabelIds(names, opts) {
|
|
1744
|
+
if (names.length === 0) return [];
|
|
1745
|
+
const labels = await listLinearLabels(opts);
|
|
1746
|
+
return names.map((name) => {
|
|
1747
|
+
const s = name.trim();
|
|
1748
|
+
try {
|
|
1749
|
+
return resolveLinearLabel(labels, s).id;
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
if (UUID_RE.test(s)) return s;
|
|
1752
|
+
throw error;
|
|
1753
|
+
}
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
async function applyLinearIssueRelations(issue, add, remove, opts) {
|
|
1757
|
+
const current = [...issue.relations];
|
|
1758
|
+
for (const rel of remove ?? []) {
|
|
1759
|
+
const type = normalizeLinearRelationType(rel.type);
|
|
1760
|
+
const issueRef = rel.issue.trim();
|
|
1761
|
+
if (!issueRef) throw new Error("Linear removeRelations.issue is required (uuid or ENG-123)");
|
|
1762
|
+
const match = current.find((r) => relationMatches(r, type, issueRef));
|
|
1763
|
+
if (!match?.id) {
|
|
1764
|
+
throw new Error(
|
|
1765
|
+
`Linear relation not found: ${type} ${issueRef}. Existing: ${formatRelationList(current)}`
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
const json = await linearGraphql(ISSUE_RELATION_DELETE, { id: match.id }, opts);
|
|
1769
|
+
if (!json.issueRelationDelete?.success) {
|
|
1770
|
+
throw new Error(`Linear issueRelationDelete failed (${type} ${issueRef})`);
|
|
1771
|
+
}
|
|
1772
|
+
const idx = current.indexOf(match);
|
|
1773
|
+
if (idx >= 0) current.splice(idx, 1);
|
|
1774
|
+
}
|
|
1775
|
+
for (const rel of add ?? []) {
|
|
1776
|
+
const type = normalizeLinearRelationType(rel.type);
|
|
1777
|
+
const issueRef = rel.issue.trim();
|
|
1778
|
+
if (!issueRef) throw new Error("Linear relations.issue is required (uuid or ENG-123)");
|
|
1779
|
+
if (current.some((r) => relationMatches(r, type, issueRef))) continue;
|
|
1780
|
+
const related = await getLinearIssue(issueRef, opts);
|
|
1781
|
+
const input = linearRelationCreateInput(issue.id, type, related.id);
|
|
1782
|
+
const json = await linearGraphql(ISSUE_RELATION_CREATE, { input }, opts);
|
|
1783
|
+
if (!json.issueRelationCreate?.success) {
|
|
1784
|
+
throw new Error(`Linear issueRelationCreate failed (${type} ${related.identifier})`);
|
|
1785
|
+
}
|
|
1786
|
+
current.push({ type, issue: related, id: void 0 });
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1572
1789
|
function normalizePriority(priority) {
|
|
1573
1790
|
if (priority == null) return void 0;
|
|
1574
1791
|
if (!Number.isInteger(priority) || priority < 0 || priority > 4) {
|
|
@@ -1576,7 +1793,6 @@ function normalizePriority(priority) {
|
|
|
1576
1793
|
}
|
|
1577
1794
|
return priority;
|
|
1578
1795
|
}
|
|
1579
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1580
1796
|
function assigneeFilterKey(assignee) {
|
|
1581
1797
|
return (assignee ?? "").trim();
|
|
1582
1798
|
}
|
|
@@ -1789,8 +2005,10 @@ async function updateLinearIssue(input, opts) {
|
|
|
1789
2005
|
if (input.description !== void 0) {
|
|
1790
2006
|
mutationInput.description = input.description;
|
|
1791
2007
|
}
|
|
1792
|
-
const
|
|
1793
|
-
const
|
|
2008
|
+
const addRelations = input.relations?.length ? input.relations : void 0;
|
|
2009
|
+
const removeRelations = input.removeRelations?.length ? input.removeRelations : void 0;
|
|
2010
|
+
const needsIssue = Boolean(input.state?.trim()) || input.cycle !== void 0 || input.labels !== void 0 || Boolean(addRelations) || Boolean(removeRelations);
|
|
2011
|
+
const existing = needsIssue ? await getLinearIssue(issueId, opts) : void 0;
|
|
1794
2012
|
if (input.state?.trim()) {
|
|
1795
2013
|
const { teams } = await listLinearTeams(opts);
|
|
1796
2014
|
const teamRef = existing?.team?.key || existing?.team?.id;
|
|
@@ -1823,14 +2041,47 @@ async function updateLinearIssue(input, opts) {
|
|
|
1823
2041
|
if (input.priority !== void 0) {
|
|
1824
2042
|
mutationInput.priority = normalizePriority(input.priority);
|
|
1825
2043
|
}
|
|
1826
|
-
if (
|
|
1827
|
-
|
|
2044
|
+
if (input.labels !== void 0) {
|
|
2045
|
+
mutationInput.labelIds = await resolveLinearLabelIds(input.labels, {
|
|
2046
|
+
...opts,
|
|
2047
|
+
teamId: existing?.team?.id
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2050
|
+
if (input.project !== void 0) {
|
|
2051
|
+
mutationInput.projectId = await resolveLinearProjectId(input.project, opts);
|
|
2052
|
+
}
|
|
2053
|
+
if (input.parent !== void 0) {
|
|
2054
|
+
if (isLinearNoneToken(input.parent)) {
|
|
2055
|
+
mutationInput.parentId = null;
|
|
2056
|
+
} else {
|
|
2057
|
+
const parent = await getLinearIssue((input.parent ?? "").trim(), opts);
|
|
2058
|
+
mutationInput.parentId = parent.id;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
const hasIssueFields = Object.keys(mutationInput).length > 0;
|
|
2062
|
+
const hasRelationWrites = Boolean(addRelations || removeRelations);
|
|
2063
|
+
if (!hasIssueFields && !hasRelationWrites) {
|
|
2064
|
+
throw new Error(
|
|
2065
|
+
"linear_update_issue needs at least one of title, description, state, assignee, priority, cycle, labels, project, parent, relations, removeRelations"
|
|
2066
|
+
);
|
|
2067
|
+
}
|
|
2068
|
+
let updated;
|
|
2069
|
+
if (hasIssueFields) {
|
|
2070
|
+
const json = await linearGraphql(ISSUE_UPDATE, { id: issueId, input: mutationInput }, opts);
|
|
2071
|
+
if (!json.issueUpdate?.success || !json.issueUpdate.issue) {
|
|
2072
|
+
throw new Error("Linear issueUpdate failed");
|
|
2073
|
+
}
|
|
2074
|
+
updated = mapIssue(json.issueUpdate.issue);
|
|
1828
2075
|
}
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
2076
|
+
if (hasRelationWrites) {
|
|
2077
|
+
const base = updated ?? existing;
|
|
2078
|
+
if (!base) {
|
|
2079
|
+
throw new Error(`Linear issue not found: ${issueId}`);
|
|
2080
|
+
}
|
|
2081
|
+
await applyLinearIssueRelations(base, addRelations, removeRelations, opts);
|
|
2082
|
+
return getLinearIssue(base.id || issueId, opts);
|
|
1832
2083
|
}
|
|
1833
|
-
return
|
|
2084
|
+
return updated;
|
|
1834
2085
|
}
|
|
1835
2086
|
async function commentLinearIssue(input, opts) {
|
|
1836
2087
|
const issueId = input.id.trim();
|
|
@@ -2050,13 +2301,78 @@ async function commentGitHubIssue(input, opts) {
|
|
|
2050
2301
|
const url = stdout.trim().split(/\s+/).find((part) => /^https?:\/\//i.test(part));
|
|
2051
2302
|
return { body, url };
|
|
2052
2303
|
}
|
|
2304
|
+
function isGitHubNoneToken(value) {
|
|
2305
|
+
if (value == null) return true;
|
|
2306
|
+
const s = value.trim();
|
|
2307
|
+
return !s || /^(none|clear|unschedule|null)$/i.test(s);
|
|
2308
|
+
}
|
|
2309
|
+
function githubRelationEditFlag(type, remove = false) {
|
|
2310
|
+
const s = type.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
2311
|
+
if (s === "blockedby" || s === "blocked" || s === "dependson") {
|
|
2312
|
+
return remove ? "--remove-blocked-by" : "--add-blocked-by";
|
|
2313
|
+
}
|
|
2314
|
+
if (s === "blocks" || s === "blocking") {
|
|
2315
|
+
return remove ? "--remove-blocking" : "--add-blocking";
|
|
2316
|
+
}
|
|
2317
|
+
throw new Error(
|
|
2318
|
+
`GitHub relation type must be blocks or blockedBy (got ${type}). Parent is a separate parent= field.`
|
|
2319
|
+
);
|
|
2320
|
+
}
|
|
2321
|
+
function labelsEqual(a, b) {
|
|
2322
|
+
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
2323
|
+
}
|
|
2324
|
+
function parseProjectTitles(raw) {
|
|
2325
|
+
const out = [];
|
|
2326
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2327
|
+
const push = (name) => {
|
|
2328
|
+
const title = name.trim();
|
|
2329
|
+
const key = title.toLowerCase();
|
|
2330
|
+
if (!title || seen.has(key)) return;
|
|
2331
|
+
seen.add(key);
|
|
2332
|
+
out.push(title);
|
|
2333
|
+
};
|
|
2334
|
+
const fromItems = raw.projectItems ?? raw.projects ?? raw.projectItemsV2;
|
|
2335
|
+
if (Array.isArray(fromItems)) {
|
|
2336
|
+
for (const item of fromItems) {
|
|
2337
|
+
if (typeof item === "string") {
|
|
2338
|
+
push(item);
|
|
2339
|
+
continue;
|
|
2340
|
+
}
|
|
2341
|
+
if (!item || typeof item !== "object") continue;
|
|
2342
|
+
const rec = item;
|
|
2343
|
+
const nested = rec.project && typeof rec.project === "object" ? rec.project : rec;
|
|
2344
|
+
const title = String(nested.title ?? nested.name ?? rec.title ?? rec.name ?? "").trim();
|
|
2345
|
+
if (title) push(title);
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
return out;
|
|
2349
|
+
}
|
|
2350
|
+
async function listGitHubIssueProjects(number, cwd, repoArgs) {
|
|
2351
|
+
const result = await gh(
|
|
2352
|
+
["issue", "view", String(number), ...repoArgs, "--json", "projectItems"],
|
|
2353
|
+
cwd,
|
|
2354
|
+
{ reject: false }
|
|
2355
|
+
);
|
|
2356
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) return [];
|
|
2357
|
+
try {
|
|
2358
|
+
const parsed = JSON.parse(result.stdout);
|
|
2359
|
+
return parseProjectTitles(parsed);
|
|
2360
|
+
} catch {
|
|
2361
|
+
return [];
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2053
2364
|
async function updateGitHubIssue(input, opts) {
|
|
2054
2365
|
const number = parseGitHubIssueNumber(input.id);
|
|
2055
2366
|
const title = input.title?.trim();
|
|
2056
2367
|
const body = input.body;
|
|
2057
2368
|
const state = input.state?.trim().toLowerCase();
|
|
2058
|
-
|
|
2059
|
-
|
|
2369
|
+
const addRelations = input.relations?.length ? input.relations : void 0;
|
|
2370
|
+
const removeRelations = input.removeRelations?.length ? input.removeRelations : void 0;
|
|
2371
|
+
const hasEditFields = Boolean(title) || body !== void 0 || input.labels !== void 0 || input.project !== void 0 || input.parent !== void 0 || Boolean(addRelations) || Boolean(removeRelations);
|
|
2372
|
+
if (!hasEditFields && !state) {
|
|
2373
|
+
throw new Error(
|
|
2374
|
+
"github_update_issue needs at least one of title, body, state, labels, project, parent, relations, removeRelations"
|
|
2375
|
+
);
|
|
2060
2376
|
}
|
|
2061
2377
|
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
2062
2378
|
if (state === "closed" || state === "close") {
|
|
@@ -2072,10 +2388,46 @@ async function updateGitHubIssue(input, opts) {
|
|
|
2072
2388
|
} else if (state) {
|
|
2073
2389
|
throw new Error(`GitHub issue state must be open or closed (got ${input.state})`);
|
|
2074
2390
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2391
|
+
const args = ["issue", "edit", String(number), ...repoArgs];
|
|
2392
|
+
if (title) args.push("--title", title);
|
|
2393
|
+
if (body !== void 0) args.push("--body", body);
|
|
2394
|
+
if (input.labels !== void 0) {
|
|
2395
|
+
const existing = await getGitHubIssue(String(number), opts);
|
|
2396
|
+
const desired = input.labels.map((name) => name.trim()).filter(Boolean);
|
|
2397
|
+
const add = desired.filter((name) => !existing.labels.some((cur) => labelsEqual(cur, name)));
|
|
2398
|
+
const remove = existing.labels.filter((cur) => !desired.some((name) => labelsEqual(cur, name)));
|
|
2399
|
+
if (add.length) args.push("--add-label", add.join(","));
|
|
2400
|
+
if (remove.length) args.push("--remove-label", remove.join(","));
|
|
2401
|
+
}
|
|
2402
|
+
if (input.project !== void 0) {
|
|
2403
|
+
const current = await listGitHubIssueProjects(number, cwd, repoArgs);
|
|
2404
|
+
if (isGitHubNoneToken(input.project)) {
|
|
2405
|
+
for (const name of current) args.push("--remove-project", name);
|
|
2406
|
+
} else {
|
|
2407
|
+
const wanted = (input.project ?? "").trim();
|
|
2408
|
+
if (!current.some((name) => labelsEqual(name, wanted))) {
|
|
2409
|
+
args.push("--add-project", wanted);
|
|
2410
|
+
}
|
|
2411
|
+
for (const name of current) {
|
|
2412
|
+
if (!labelsEqual(name, wanted)) args.push("--remove-project", name);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
if (input.parent !== void 0) {
|
|
2417
|
+
if (isGitHubNoneToken(input.parent)) {
|
|
2418
|
+
args.push("--remove-parent");
|
|
2419
|
+
} else {
|
|
2420
|
+
args.push("--parent", String(parseGitHubIssueNumber(input.parent ?? "")));
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
for (const rel of removeRelations ?? []) {
|
|
2424
|
+
args.push(githubRelationEditFlag(rel.type, true), String(parseGitHubIssueNumber(rel.issue)));
|
|
2425
|
+
}
|
|
2426
|
+
for (const rel of addRelations ?? []) {
|
|
2427
|
+
args.push(githubRelationEditFlag(rel.type, false), String(parseGitHubIssueNumber(rel.issue)));
|
|
2428
|
+
}
|
|
2429
|
+
const editChanged = args.length > 3 + repoArgs.length;
|
|
2430
|
+
if (editChanged) {
|
|
2079
2431
|
requireGhOk(await gh(args, cwd, { reject: false }), `GitHub edit #${number}`);
|
|
2080
2432
|
}
|
|
2081
2433
|
return getGitHubIssue(String(number), opts);
|
|
@@ -3671,14 +4023,23 @@ function registerAbleTimeTools(server) {
|
|
|
3671
4023
|
}
|
|
3672
4024
|
}
|
|
3673
4025
|
);
|
|
4026
|
+
const relationInputSchema = z4.object({
|
|
4027
|
+
type: z4.string().describe("blockedBy (this waits on issue) or blocks (issue waits on this)."),
|
|
4028
|
+
issue: z4.string().describe("Related task id or reference (CRM-232).")
|
|
4029
|
+
});
|
|
3674
4030
|
server.tool(
|
|
3675
4031
|
"abletime_update_task",
|
|
3676
|
-
|
|
4032
|
+
'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.',
|
|
3677
4033
|
{
|
|
3678
4034
|
id: z4.string(),
|
|
3679
4035
|
title: z4.string().optional(),
|
|
3680
4036
|
description: z4.string().optional(),
|
|
3681
|
-
state: z4.string().optional()
|
|
4037
|
+
state: z4.string().optional(),
|
|
4038
|
+
labels: z4.array(z4.string()).optional().describe("Replace tags/labels. Empty array clears when the host accepts it."),
|
|
4039
|
+
project: z4.string().nullable().optional().describe("Project name or id. AbleTime tasks stay in a project."),
|
|
4040
|
+
parent: z4.string().nullable().optional().describe('Parent task id or reference (CRM-232), or "none" to unset.'),
|
|
4041
|
+
relations: z4.array(relationInputSchema).optional().describe("Set a wait-on dependency: blockedBy or blocks."),
|
|
4042
|
+
removeRelations: z4.array(relationInputSchema).optional().describe("Clear a wait-on dependency.")
|
|
3682
4043
|
},
|
|
3683
4044
|
async (args) => {
|
|
3684
4045
|
try {
|
|
@@ -3812,14 +4173,23 @@ function registerGithubIssueTools(server) {
|
|
|
3812
4173
|
}
|
|
3813
4174
|
}
|
|
3814
4175
|
);
|
|
4176
|
+
const relationInputSchema = z5.object({
|
|
4177
|
+
type: z5.string().describe("blocks or blockedBy (this issue's view)."),
|
|
4178
|
+
issue: z5.string().describe("Related issue #123 or URL.")
|
|
4179
|
+
});
|
|
3815
4180
|
server.tool(
|
|
3816
4181
|
"github_update_issue",
|
|
3817
|
-
|
|
4182
|
+
'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.',
|
|
3818
4183
|
{
|
|
3819
4184
|
id: z5.string(),
|
|
3820
4185
|
title: z5.string().optional(),
|
|
3821
4186
|
body: z5.string().optional(),
|
|
3822
4187
|
state: z5.string().optional().describe("open or closed"),
|
|
4188
|
+
labels: z5.array(z5.string()).optional().describe("Replace all labels. Empty array clears. Call github_get_issue first to keep existing."),
|
|
4189
|
+
project: z5.string().nullable().optional().describe('GitHub Project title, or "none" to remove current projects.'),
|
|
4190
|
+
parent: z5.string().nullable().optional().describe('Parent issue #123 or URL, or "none" to unset.'),
|
|
4191
|
+
relations: z5.array(relationInputSchema).optional().describe("Add blockedBy or blocks relations (issue #123 or URL)."),
|
|
4192
|
+
removeRelations: z5.array(relationInputSchema).optional().describe("Remove blockedBy or blocks relations."),
|
|
3823
4193
|
repoPath: repoPathSchema
|
|
3824
4194
|
},
|
|
3825
4195
|
async (args) => {
|
|
@@ -3949,9 +4319,13 @@ function registerLinearTools(server) {
|
|
|
3949
4319
|
}
|
|
3950
4320
|
}
|
|
3951
4321
|
);
|
|
4322
|
+
const relationInputSchema = z6.object({
|
|
4323
|
+
type: z6.string().describe("blocks, blockedBy, related, duplicate, or duplicateOf (this issue's view)."),
|
|
4324
|
+
issue: z6.string().describe("Related issue uuid or identifier (ENG-123).")
|
|
4325
|
+
});
|
|
3952
4326
|
server.tool(
|
|
3953
4327
|
"linear_update_issue",
|
|
3954
|
-
'Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, priority, and/or
|
|
4328
|
+
'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.',
|
|
3955
4329
|
{
|
|
3956
4330
|
id: z6.string(),
|
|
3957
4331
|
title: z6.string().optional(),
|
|
@@ -3959,7 +4333,12 @@ function registerLinearTools(server) {
|
|
|
3959
4333
|
state: z6.string().optional(),
|
|
3960
4334
|
assignee: z6.string().nullable().optional(),
|
|
3961
4335
|
priority: prioritySchema,
|
|
3962
|
-
cycle: z6.string().nullable().optional().describe('Cycle name, number, "current"/"active", or "none" to unschedule.')
|
|
4336
|
+
cycle: z6.string().nullable().optional().describe('Cycle name, number, "current"/"active", or "none" to unschedule.'),
|
|
4337
|
+
labels: z6.array(z6.string()).optional().describe("Replace all labels (names or ids). Empty array clears. Call linear_get_issue first to keep existing."),
|
|
4338
|
+
project: z6.string().nullable().optional().describe('Project name, id, slug, or "none" to unassign.'),
|
|
4339
|
+
parent: z6.string().nullable().optional().describe('Parent issue uuid or identifier (ENG-123), or "none" to unset.'),
|
|
4340
|
+
relations: z6.array(relationInputSchema).optional().describe("Add relations from this issue's view (blocks, blockedBy, related, duplicate, duplicateOf)."),
|
|
4341
|
+
removeRelations: z6.array(relationInputSchema).optional().describe("Remove relations by type + related issue (uuid or ENG-123).")
|
|
3963
4342
|
},
|
|
3964
4343
|
async (args) => {
|
|
3965
4344
|
try {
|
|
@@ -5194,7 +5573,7 @@ async function startMcpServer() {
|
|
|
5194
5573
|
);
|
|
5195
5574
|
server.tool(
|
|
5196
5575
|
"send_to_thread",
|
|
5197
|
-
'
|
|
5576
|
+
'Steer a prompt on a worktree thread chat (Settings \u2192 Follow-up, default steer: interrupt and start now, skip the inbox). Use after create_thread to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not send_to_thread to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
|
|
5198
5577
|
{
|
|
5199
5578
|
ref: z9.string(),
|
|
5200
5579
|
prompt: z9.string(),
|
|
@@ -5207,7 +5586,9 @@ async function startMcpServer() {
|
|
|
5207
5586
|
orch.stop(ref, { clearQueue: true, notifyParent: false });
|
|
5208
5587
|
}
|
|
5209
5588
|
}
|
|
5210
|
-
const thread = await orch.send(ref, prompt
|
|
5589
|
+
const thread = await orch.send(ref, prompt, {
|
|
5590
|
+
followUp: resolveOrchChildFollowUp()
|
|
5591
|
+
});
|
|
5211
5592
|
return mcpJson({
|
|
5212
5593
|
id: thread.id,
|
|
5213
5594
|
status: thread.status,
|
|
@@ -5218,7 +5599,7 @@ async function startMcpServer() {
|
|
|
5218
5599
|
);
|
|
5219
5600
|
server.tool(
|
|
5220
5601
|
"wait_for_turn",
|
|
5221
|
-
"Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. stillRunning is the source of truth \u2014 if false, the child is not working (do not say it is waiting for a gate). If stillRunning is true, progress is tools/thinking (or \u201Cqueued, waiting for a concurrency slot\u201D if it has not started). Call wait_for_turn again. Do not
|
|
5602
|
+
"Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. stillRunning is the source of truth \u2014 if false, the child is not working (do not say it is waiting for a gate). If stillRunning is true, progress is tools/thinking (or \u201Cqueued, waiting for a concurrency slot\u201D if it has not started). Call wait_for_turn again. Do not send_to_thread a check-in (that steers / interrupts), force_stop, or assume a hang. On status error, lastError/text is the failure. On status stopped or broken, the child did not finish \u2014 resume with send_to_thread or tell the user; do not treat that as success. When finished, usage is the last agent turn\u2019s tokens + costUsd (when the provider reported cost).",
|
|
5222
5603
|
{
|
|
5223
5604
|
ref: z9.string(),
|
|
5224
5605
|
timeoutMs: z9.number().optional()
|
|
@@ -5375,7 +5756,7 @@ async function startMcpServer() {
|
|
|
5375
5756
|
);
|
|
5376
5757
|
server.tool(
|
|
5377
5758
|
"ask_git",
|
|
5378
|
-
"Commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` even when origin is SSH / Settings \u2192 Git is SSH). When dirty,
|
|
5759
|
+
"Commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` even when origin is SSH / Settings \u2192 Git is SSH). When dirty, steers the worktree agent to commit (Settings \u2192 Follow-up, default interrupt now); then wait_for_turn (loop while stillRunning). Do not start a checks loop on a plain push \u2014 only if the user gave a goal (Greptile 5/5, CI green). Pass a worktree thread ref (not the orchestrator). action=ready-for-review and action=merge only when the user explicitly asked (or clicked the desktop button). Do not run git or gh from the orchestration cwd. If this tool errors that the GraphQL/PR body is too long, the branch is already pushed \u2014 have the worktree agent retry `gh pr create --body-file` with a short description (GitHub limit 65,536 characters). Do not invent SSH/auth failures from that error.",
|
|
5379
5760
|
{
|
|
5380
5761
|
ref: z9.string().describe("Worktree thread id/ref"),
|
|
5381
5762
|
action: z9.enum(AGENT_GIT_ACTIONS).describe(
|
|
@@ -7997,6 +8378,8 @@ async function startSlackRelayServer(opts) {
|
|
|
7997
8378
|
}
|
|
7998
8379
|
export {
|
|
7999
8380
|
ABLETIME_MCP_PATH,
|
|
8381
|
+
ABLETIME_MCP_PM_PATH,
|
|
8382
|
+
ABLETIME_PM_TOOL_NAMES,
|
|
8000
8383
|
AGENT_GIT_ACTIONS,
|
|
8001
8384
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
8002
8385
|
ATTACHMENTS_DIR,
|
|
@@ -8086,8 +8469,10 @@ export {
|
|
|
8086
8469
|
WORKTREE_GITHUB_MCP_TOOLS,
|
|
8087
8470
|
WORKTREE_LINEAR_MCP_TOOLS,
|
|
8088
8471
|
WORKTREE_MCP_TOOLS,
|
|
8472
|
+
ableTimeDependencyTarget,
|
|
8089
8473
|
abletimeMcpRequest,
|
|
8090
8474
|
abletimeMcpUrl,
|
|
8475
|
+
abletimeToolUsesPm,
|
|
8091
8476
|
ackSlackInboundSeen,
|
|
8092
8477
|
addBoardPin,
|
|
8093
8478
|
addPrStackLayer,
|
|
@@ -8360,6 +8745,7 @@ export {
|
|
|
8360
8745
|
git,
|
|
8361
8746
|
githubAgentGitEnv,
|
|
8362
8747
|
githubHttpsInsteadOfEntries,
|
|
8748
|
+
githubRelationEditFlag,
|
|
8363
8749
|
globalAgentCwd,
|
|
8364
8750
|
groupHomeBoardWorktrees,
|
|
8365
8751
|
handleSlackInbound,
|
|
@@ -8409,6 +8795,7 @@ export {
|
|
|
8409
8795
|
isInternalAgentStatusText,
|
|
8410
8796
|
isIssueSourceConnected,
|
|
8411
8797
|
isLinearConnected,
|
|
8798
|
+
isLinearNoneToken,
|
|
8412
8799
|
isLinearOAuthCancelled,
|
|
8413
8800
|
isOptionalServiceId,
|
|
8414
8801
|
isOrchestratorCapableAgent,
|
|
@@ -8446,6 +8833,7 @@ export {
|
|
|
8446
8833
|
linearGraphql,
|
|
8447
8834
|
linearOAuthAuthorizeUrl,
|
|
8448
8835
|
linearOAuthCredentials,
|
|
8836
|
+
linearRelationCreateInput,
|
|
8449
8837
|
linearTicketIdFromThread,
|
|
8450
8838
|
listAbleTimeAssignedIssues,
|
|
8451
8839
|
listAbleTimeProjects,
|
|
@@ -8514,6 +8902,7 @@ export {
|
|
|
8514
8902
|
nonInteractiveGitProcessEnv,
|
|
8515
8903
|
normalizeAbleTimeHost,
|
|
8516
8904
|
normalizeCodeSelection,
|
|
8905
|
+
normalizeLinearRelationType,
|
|
8517
8906
|
normalizeParseResult,
|
|
8518
8907
|
normalizeServiceOrigin,
|
|
8519
8908
|
normalizeThinkingEffort,
|
|
@@ -8607,11 +8996,14 @@ export {
|
|
|
8607
8996
|
resolveGithubAgentToken,
|
|
8608
8997
|
resolveGithubRepoSlug,
|
|
8609
8998
|
resolveLinearCycle,
|
|
8999
|
+
resolveLinearLabel,
|
|
9000
|
+
resolveLinearProject,
|
|
8610
9001
|
resolveLinearState,
|
|
8611
9002
|
resolveLinearTeam,
|
|
8612
9003
|
resolveLoginCommand,
|
|
8613
9004
|
resolveNewOrchestratorOptions,
|
|
8614
9005
|
resolveNewThreadOptions,
|
|
9006
|
+
resolveOrchChildFollowUp,
|
|
8615
9007
|
resolveOrchestratorDefaults,
|
|
8616
9008
|
resolvePlanMarkdown,
|
|
8617
9009
|
resolvePrSelector,
|