@sideboard-ai/core 0.1.315 → 0.1.317
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-QQZWYJXC.js → abletime-NZ2ABWAL.js} +3 -3
- package/dist/{abletime-QK3KMRQD.js → abletime-TKAFNXCM.js} +3 -3
- package/dist/{abletime-oauth-CAHTMB4V.js → abletime-oauth-CBFFBINA.js} +2 -2
- package/dist/{abletime-oauth-I2RRPVKT.js → abletime-oauth-S4AXA4WR.js} +2 -2
- package/dist/{abletime-rest-5V4KGMSY.js → abletime-rest-NSOP72AI.js} +2 -2
- package/dist/{abletime-rest-W5TLQK7C.js → abletime-rest-ORBELZQO.js} +2 -2
- package/dist/{agents-Y3M3EKLY.js → agents-BJVSYC6Y.js} +4 -4
- package/dist/{agents-5XJ7EHGU.js → agents-KABJUDPI.js} +4 -4
- package/dist/{chunk-IODJCVMN.js → chunk-4XZJYBO2.js} +6 -34
- package/dist/{chunk-JRZTJ4F7.js → chunk-7SCVGGF6.js} +4 -4
- package/dist/{chunk-5CTG5W7S.js → chunk-A46CIARU.js} +1 -1
- package/dist/{chunk-PN32Z4JL.js → chunk-A6C67DY5.js} +2 -2
- package/dist/{chunk-CYPFOIUK.js → chunk-BXTISNKW.js} +16 -16
- package/dist/{chunk-7J2KOIYD.js → chunk-CCUGIVYN.js} +2 -2
- package/dist/{chunk-DV6ID6CK.js → chunk-ELBK34TY.js} +4 -4
- package/dist/{chunk-FVTR54LY.js → chunk-GH7UMULC.js} +4 -4
- package/dist/{chunk-MKGSMPWB.js → chunk-GXSV3X3L.js} +2 -2
- package/dist/{chunk-U35AHE2P.js → chunk-HOLB6ZFZ.js} +33 -2
- package/dist/{chunk-72OWKHFJ.js → chunk-I5CLCWUK.js} +2 -2
- package/dist/{chunk-YR7NZYUT.js → chunk-NRSC4N5X.js} +6 -34
- package/dist/{chunk-GFM63DTU.js → chunk-PX324N7P.js} +2 -2
- package/dist/{chunk-BMORGDYI.js → chunk-U53JICJ2.js} +18 -17
- package/dist/{chunk-NZVD5EVX.js → chunk-VVV7EI6T.js} +2 -1
- package/dist/{chunk-Y4WARKPW.js → chunk-VZAJVX36.js} +2 -1
- package/dist/{chunk-NKVNHBDB.js → chunk-XWUDFLBX.js} +3 -3
- package/dist/{chunk-CHUPIJP7.js → chunk-YEIHCSZS.js} +63 -2
- package/dist/{chunk-BPBDGJ43.js → chunk-YN6XONV5.js} +1 -1
- package/dist/{conductor-IMKNYQWZ.js → conductor-NIUBJLTS.js} +4 -4
- package/dist/{conductor-DKWTQADV.js → conductor-TP2NA44I.js} +4 -4
- package/dist/{coordinator-prompt-HUZC4DZ2.js → coordinator-prompt-3CT3D5UM.js} +3 -3
- package/dist/{coordinator-prompt-SPOYUZG5.js → coordinator-prompt-FQC2SZPP.js} +3 -3
- package/dist/{global-workspace-3NNDDTFD.js → global-workspace-72K6D354.js} +3 -3
- package/dist/{global-workspace-SK4JQBPN.js → global-workspace-BUWMRNW5.js} +3 -3
- package/dist/index.cjs +360 -42
- package/dist/index.d.cts +11 -5
- package/dist/index.d.ts +11 -5
- package/dist/index.js +301 -21
- package/dist/mcp/run-stdio.cjs +38 -34
- package/dist/mcp/run-stdio.js +9 -9
- package/dist/{orchestrator-4XFRK57E.js → orchestrator-E72HYGTB.js} +6 -6
- package/dist/{orchestrator-ZRMZ4TCL.js → orchestrator-N54V3B76.js} +6 -6
- package/dist/{thread-store-5OF6M6VZ.js → thread-store-7BBA63KV.js} +1 -1
- package/dist/{thread-store-GOVVXQV2.js → thread-store-PLNIWFZ4.js} +1 -1
- package/dist/{workspaces-EYNZDL45.js → workspaces-2V5UYPYC.js} +3 -3
- package/dist/{workspaces-52L6NZ3P.js → workspaces-GAUHXBUL.js} +3 -3
- package/dist/{worktree-BP5ICYQ2.js → worktree-6FUQ7FEF.js} +2 -2
- package/dist/{worktree-UCOSAI7J.js → worktree-XJKLDYOL.js} +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4112,6 +4112,38 @@ var init_caffeinate_hold = __esm({
|
|
|
4112
4112
|
}
|
|
4113
4113
|
});
|
|
4114
4114
|
|
|
4115
|
+
// src/review/review-write-gate.ts
|
|
4116
|
+
function hasLiveReviewSibling(thread, siblings = []) {
|
|
4117
|
+
const selfId = thread.id;
|
|
4118
|
+
return siblings.some(
|
|
4119
|
+
(t) => t.id !== selfId && t.status !== "archived" && t.title.trim() === REVIEW_TAB_TITLE
|
|
4120
|
+
);
|
|
4121
|
+
}
|
|
4122
|
+
function isReviewWriteGatedThread(thread, siblings) {
|
|
4123
|
+
if (thread.sourceType === "pr" || thread.title.trim() === REVIEW_TAB_TITLE) {
|
|
4124
|
+
return true;
|
|
4125
|
+
}
|
|
4126
|
+
return hasLiveReviewSibling(thread, siblings);
|
|
4127
|
+
}
|
|
4128
|
+
function formatReviewWriteGateDirective() {
|
|
4129
|
+
return [
|
|
4130
|
+
"Review writes are public. The PR or ticket author sees GitHub/Linear/AbleTime comments, ticket updates, and submitted reviews immediately \u2014 a draft will confuse them.",
|
|
4131
|
+
"Write the recommendation in chat so the user can work through the feedback first. Do not call github_comment, linear_comment, abletime_comment, github_update_issue, linear_update_issue, abletime_update_task, or run `gh pr review` / `gh pr comment`, until they type a next step that asks you to post.",
|
|
4132
|
+
"After the review is in chat, stop. Do not call ask_user. The user needs time to read and will type the next steps. If they already asked you to post a specific comment or update in this turn, that is confirmation.",
|
|
4133
|
+
"Do not request reviewers, change labels, or assign yourself unless they asked \u2014 those also notify the author."
|
|
4134
|
+
].join("\n");
|
|
4135
|
+
}
|
|
4136
|
+
function formatReviewWriteGateReminder() {
|
|
4137
|
+
return "Review writes: keep the review in chat. Do not ask_user after it \u2014 they read and type next steps. Do not github_comment / linear_comment / *_update_* / `gh pr review` on the PR or ticket until they ask. Authors see those immediately.";
|
|
4138
|
+
}
|
|
4139
|
+
var REVIEW_TAB_TITLE;
|
|
4140
|
+
var init_review_write_gate = __esm({
|
|
4141
|
+
"src/review/review-write-gate.ts"() {
|
|
4142
|
+
"use strict";
|
|
4143
|
+
REVIEW_TAB_TITLE = "Review";
|
|
4144
|
+
}
|
|
4145
|
+
});
|
|
4146
|
+
|
|
4115
4147
|
// src/git/team-meta.ts
|
|
4116
4148
|
var SOCCER_TEAM_META;
|
|
4117
4149
|
var init_team_meta = __esm({
|
|
@@ -4983,11 +5015,12 @@ function branchDisplayLabel(branchName, worktreePath) {
|
|
|
4983
5015
|
}
|
|
4984
5016
|
function taskPurposeTitle(title) {
|
|
4985
5017
|
const trimmed = title?.trim() ?? "";
|
|
4986
|
-
if (!trimmed || teamSlugFromName(trimmed)) return null;
|
|
5018
|
+
if (!trimmed || teamSlugFromName(trimmed) || trimmed === REVIEW_TAB_TITLE) return null;
|
|
4987
5019
|
return trimmed;
|
|
4988
5020
|
}
|
|
4989
5021
|
function threadDisplayLabel(thread) {
|
|
4990
|
-
|
|
5022
|
+
const titled = thread.title?.trim() ?? "";
|
|
5023
|
+
if (thread.userSetTitle && titled && titled !== REVIEW_TAB_TITLE) return titled;
|
|
4991
5024
|
if (thread.prTitle?.trim()) return thread.prTitle.trim();
|
|
4992
5025
|
if (isPlaceholderBranch(thread.branchName, thread.worktreePath)) {
|
|
4993
5026
|
const purpose = taskPurposeTitle(thread.title);
|
|
@@ -5003,6 +5036,35 @@ function worktreeDisplayLabelForGroup(threads) {
|
|
|
5003
5036
|
const canonical = [...threads].sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
|
|
5004
5037
|
return threadDisplayLabel(canonical);
|
|
5005
5038
|
}
|
|
5039
|
+
function worktreeAnchorLabel(threads) {
|
|
5040
|
+
if (threads.length === 0) return { title: "Worktree", subtitle: null };
|
|
5041
|
+
const canonical = [...threads].sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
|
|
5042
|
+
const pr = threads.map((t) => t.prTitle?.trim()).find((v) => Boolean(v)) ?? "";
|
|
5043
|
+
const branch = (canonical.branchName ?? "").trim();
|
|
5044
|
+
const placeholder = isPlaceholderBranch(branch, canonical.worktreePath);
|
|
5045
|
+
const display = branchDisplayLabel(canonical.branchName, canonical.worktreePath);
|
|
5046
|
+
const realBranch = !placeholder && branch && branch !== "HEAD" ? branch : null;
|
|
5047
|
+
if (pr) {
|
|
5048
|
+
return {
|
|
5049
|
+
title: pr,
|
|
5050
|
+
subtitle: realBranch && realBranch !== pr ? realBranch : null
|
|
5051
|
+
};
|
|
5052
|
+
}
|
|
5053
|
+
if (placeholder) {
|
|
5054
|
+
const purpose = taskPurposeTitle(canonical.title);
|
|
5055
|
+
if (purpose) return { title: purpose, subtitle: null };
|
|
5056
|
+
}
|
|
5057
|
+
if (display) {
|
|
5058
|
+
return {
|
|
5059
|
+
title: display,
|
|
5060
|
+
subtitle: realBranch && realBranch !== display ? realBranch : null
|
|
5061
|
+
};
|
|
5062
|
+
}
|
|
5063
|
+
return {
|
|
5064
|
+
title: worktreeNameFromPath(canonical.worktreePath),
|
|
5065
|
+
subtitle: realBranch
|
|
5066
|
+
};
|
|
5067
|
+
}
|
|
5006
5068
|
function agentNicknameFromWorktreePath(worktreePath) {
|
|
5007
5069
|
const dir = worktreeNameFromPath(worktreePath);
|
|
5008
5070
|
if (!soccerTokenFromTakenSlug(dir)) return null;
|
|
@@ -5053,6 +5115,7 @@ var WORKSPACE_TAG_LIMIT, WORKSPACE_TAG_MAX_LENGTH;
|
|
|
5053
5115
|
var init_worktree_labels = __esm({
|
|
5054
5116
|
"src/git/worktree-labels.ts"() {
|
|
5055
5117
|
"use strict";
|
|
5118
|
+
init_review_write_gate();
|
|
5056
5119
|
init_teams();
|
|
5057
5120
|
WORKSPACE_TAG_LIMIT = 8;
|
|
5058
5121
|
WORKSPACE_TAG_MAX_LENGTH = 32;
|
|
@@ -20775,6 +20838,7 @@ function worktreeBindingFrom(from) {
|
|
|
20775
20838
|
prIsInMergeQueue: from.prIsInMergeQueue,
|
|
20776
20839
|
prLabels: from.prLabels,
|
|
20777
20840
|
prChecksFailed: from.prChecksFailed,
|
|
20841
|
+
issueBlockedBy: from.issueBlockedBy,
|
|
20778
20842
|
skipAutoArchiveOnMerge: from.skipAutoArchiveOnMerge,
|
|
20779
20843
|
stackId: from.stackId,
|
|
20780
20844
|
stackLayer: from.stackLayer,
|
|
@@ -20886,38 +20950,6 @@ var init_chat_tabs = __esm({
|
|
|
20886
20950
|
}
|
|
20887
20951
|
});
|
|
20888
20952
|
|
|
20889
|
-
// src/review/review-write-gate.ts
|
|
20890
|
-
function hasLiveReviewSibling(thread, siblings = []) {
|
|
20891
|
-
const selfId = thread.id;
|
|
20892
|
-
return siblings.some(
|
|
20893
|
-
(t) => t.id !== selfId && t.status !== "archived" && t.title.trim() === REVIEW_TAB_TITLE
|
|
20894
|
-
);
|
|
20895
|
-
}
|
|
20896
|
-
function isReviewWriteGatedThread(thread, siblings) {
|
|
20897
|
-
if (thread.sourceType === "pr" || thread.title.trim() === REVIEW_TAB_TITLE) {
|
|
20898
|
-
return true;
|
|
20899
|
-
}
|
|
20900
|
-
return hasLiveReviewSibling(thread, siblings);
|
|
20901
|
-
}
|
|
20902
|
-
function formatReviewWriteGateDirective() {
|
|
20903
|
-
return [
|
|
20904
|
-
"Review writes are public. The PR or ticket author sees GitHub/Linear/AbleTime comments, ticket updates, and submitted reviews immediately \u2014 a draft will confuse them.",
|
|
20905
|
-
"Write the recommendation in chat so the user can work through the feedback first. Do not call github_comment, linear_comment, abletime_comment, github_update_issue, linear_update_issue, abletime_update_task, or run `gh pr review` / `gh pr comment`, until they type a next step that asks you to post.",
|
|
20906
|
-
"After the review is in chat, stop. Do not call ask_user. The user needs time to read and will type the next steps. If they already asked you to post a specific comment or update in this turn, that is confirmation.",
|
|
20907
|
-
"Do not request reviewers, change labels, or assign yourself unless they asked \u2014 those also notify the author."
|
|
20908
|
-
].join("\n");
|
|
20909
|
-
}
|
|
20910
|
-
function formatReviewWriteGateReminder() {
|
|
20911
|
-
return "Review writes: keep the review in chat. Do not ask_user after it \u2014 they read and type next steps. Do not github_comment / linear_comment / *_update_* / `gh pr review` on the PR or ticket until they ask. Authors see those immediately.";
|
|
20912
|
-
}
|
|
20913
|
-
var REVIEW_TAB_TITLE;
|
|
20914
|
-
var init_review_write_gate = __esm({
|
|
20915
|
-
"src/review/review-write-gate.ts"() {
|
|
20916
|
-
"use strict";
|
|
20917
|
-
REVIEW_TAB_TITLE = "Review";
|
|
20918
|
-
}
|
|
20919
|
-
});
|
|
20920
|
-
|
|
20921
20953
|
// src/review/review-request-template.ts
|
|
20922
20954
|
function wrapReviewSkillMarkdown(body) {
|
|
20923
20955
|
const trimmed = body.replace(/^\uFEFF/, "").trim();
|
|
@@ -23916,6 +23948,7 @@ function restTaskToRaw(raw, users) {
|
|
|
23916
23948
|
projectId: asString(rec.projectId),
|
|
23917
23949
|
categoryId: asString(rec.projectCategoryId) || asString(rec.categoryId),
|
|
23918
23950
|
parentId: asString(rec.parentTaskId) || asString(rec.parentId),
|
|
23951
|
+
depends_on: rec.dependencyParentTimeflowTaskId ?? null,
|
|
23919
23952
|
tags: rec.tags,
|
|
23920
23953
|
comments,
|
|
23921
23954
|
attachments: rec.attachments ?? rec.files ?? rec.media,
|
|
@@ -33550,6 +33583,7 @@ __export(index_exports, {
|
|
|
33550
33583
|
recordSlackOutboundWatch: () => recordSlackOutboundWatch,
|
|
33551
33584
|
refreshBrightsyAccessToken: () => refreshBrightsyAccessToken,
|
|
33552
33585
|
refreshGitHubAuth: () => refreshGitHubAuth,
|
|
33586
|
+
refreshWorktreeIssueBlocked: () => refreshWorktreeIssueBlocked,
|
|
33553
33587
|
registerBrightsyOAuthClient: () => registerBrightsyOAuthClient,
|
|
33554
33588
|
registerPackagedUserMcpClients: () => registerPackagedUserMcpClients,
|
|
33555
33589
|
releaseCaffeinateHoldForThread: () => releaseCaffeinateHoldForThread,
|
|
@@ -35491,15 +35525,13 @@ function listPhoneSidebar(viewerLogin = phoneViewerLogin()) {
|
|
|
35491
35525
|
path: path2,
|
|
35492
35526
|
worktrees: groupHomeBoardWorktrees(repoThreads).map((group) => {
|
|
35493
35527
|
const tags = workspaceTagsFromGroup(group);
|
|
35494
|
-
const badge = classifyWorktreeListBadges(
|
|
35495
|
-
|
|
35496
|
-
viewerLogin,
|
|
35497
|
-
resolveProjectReviewLabel(settings, path2)
|
|
35498
|
-
)[0];
|
|
35528
|
+
const badge = classifyWorktreeListBadges(group, viewerLogin, resolveProjectReviewLabel(settings, path2))[0];
|
|
35529
|
+
const blockedBy = issueBlockedByIds(group);
|
|
35499
35530
|
return {
|
|
35500
|
-
label:
|
|
35531
|
+
label: worktreeAnchorLabel(group).title,
|
|
35501
35532
|
chats: group.map(summarize),
|
|
35502
35533
|
...tags.length > 0 ? { tags } : {},
|
|
35534
|
+
...blockedBy.length > 0 ? { blockedBy } : {},
|
|
35503
35535
|
...badge ? { badge: { id: badge.id, label: badge.label, title: badge.title, ...badge.conflict ? { conflict: true } : {} } } : {}
|
|
35504
35536
|
};
|
|
35505
35537
|
})
|
|
@@ -35576,7 +35608,7 @@ function projectPlace(thread) {
|
|
|
35576
35608
|
return {
|
|
35577
35609
|
kind: "project",
|
|
35578
35610
|
repoPath: thread.repoPath,
|
|
35579
|
-
worktree:
|
|
35611
|
+
worktree: worktreeAnchorLabel(group.length > 0 ? group : [thread]).title
|
|
35580
35612
|
};
|
|
35581
35613
|
}
|
|
35582
35614
|
function assertKnownProject(repoPath) {
|
|
@@ -38923,7 +38955,7 @@ function phoneBoardKey(sidebar) {
|
|
|
38923
38955
|
const orchestration = sidebar.orchestration.map(chatKey).join("\n");
|
|
38924
38956
|
const projects = sidebar.projects.map((project) => {
|
|
38925
38957
|
const trees = project.worktrees.map(
|
|
38926
|
-
(tree) => `${tree.label} ${tree.badge?.id ?? ""} ${(tree.tags ?? []).join(",")} ${tree.chats.map(chatKey).join("\n")}`
|
|
38958
|
+
(tree) => `${tree.label} ${tree.badge?.id ?? ""} ${(tree.tags ?? []).join(",")} ${(tree.blockedBy ?? []).join(",")} ${tree.chats.map(chatKey).join("\n")}`
|
|
38927
38959
|
).join("\n");
|
|
38928
38960
|
return `${project.path} ${project.name}
|
|
38929
38961
|
${trees}`;
|
|
@@ -45511,6 +45543,291 @@ init_pilot_tools();
|
|
|
45511
45543
|
// src/index.ts
|
|
45512
45544
|
init_profile();
|
|
45513
45545
|
init_home_board();
|
|
45546
|
+
|
|
45547
|
+
// src/integrations/abletime-blocked.ts
|
|
45548
|
+
init_abletime_mcp();
|
|
45549
|
+
init_abletime_rest();
|
|
45550
|
+
function asRecord18(value) {
|
|
45551
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
45552
|
+
}
|
|
45553
|
+
function asString5(value) {
|
|
45554
|
+
return typeof value === "string" ? value.trim() : "";
|
|
45555
|
+
}
|
|
45556
|
+
function firstString5(record, keys) {
|
|
45557
|
+
if (!record) return "";
|
|
45558
|
+
for (const key of keys) {
|
|
45559
|
+
const value = asString5(record[key]);
|
|
45560
|
+
if (value) return value;
|
|
45561
|
+
}
|
|
45562
|
+
return "";
|
|
45563
|
+
}
|
|
45564
|
+
function firstRecord2(record, keys) {
|
|
45565
|
+
if (!record) return null;
|
|
45566
|
+
for (const key of keys) {
|
|
45567
|
+
const value = asRecord18(record[key]);
|
|
45568
|
+
if (value) return value;
|
|
45569
|
+
}
|
|
45570
|
+
return null;
|
|
45571
|
+
}
|
|
45572
|
+
function identifierFromUnknown(value) {
|
|
45573
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
45574
|
+
const id2 = String(value).trim();
|
|
45575
|
+
return id2 ? [id2] : [];
|
|
45576
|
+
}
|
|
45577
|
+
if (Array.isArray(value)) return value.flatMap(identifierFromUnknown);
|
|
45578
|
+
const rec = asRecord18(value);
|
|
45579
|
+
if (!rec) return [];
|
|
45580
|
+
for (const key of ["nodes", "data", "tasks", "items"]) {
|
|
45581
|
+
if (Array.isArray(rec[key])) return identifierFromUnknown(rec[key]);
|
|
45582
|
+
}
|
|
45583
|
+
const id = firstString5(rec, ["identifier", "reference", "ref", "key", "code", "number", "id"]);
|
|
45584
|
+
return id ? [id] : [];
|
|
45585
|
+
}
|
|
45586
|
+
function ableTimeBlockedByIdentifiers(raw) {
|
|
45587
|
+
const record = asRecord18(raw);
|
|
45588
|
+
const nested = firstRecord2(record, ["task", "data"]) ?? record;
|
|
45589
|
+
if (!nested) return [];
|
|
45590
|
+
const value = nested.blocked_by ?? nested.blockedBy ?? nested.depends_on ?? nested.dependsOn ?? nested.dependencyParentTimeflowTaskId ?? nested.wait_on ?? nested.waitOn;
|
|
45591
|
+
const out = [];
|
|
45592
|
+
const seen = /* @__PURE__ */ new Set();
|
|
45593
|
+
for (const id of identifierFromUnknown(value)) {
|
|
45594
|
+
const key = id.toLowerCase();
|
|
45595
|
+
if (seen.has(key)) continue;
|
|
45596
|
+
seen.add(key);
|
|
45597
|
+
out.push(id);
|
|
45598
|
+
}
|
|
45599
|
+
return out;
|
|
45600
|
+
}
|
|
45601
|
+
function taskReference(raw) {
|
|
45602
|
+
const record = asRecord18(raw);
|
|
45603
|
+
const nested = firstRecord2(record, ["task", "data"]) ?? record;
|
|
45604
|
+
return firstString5(nested, ["taskRef", "reference", "identifier"]);
|
|
45605
|
+
}
|
|
45606
|
+
async function getAbleTimeIssueBlockedBy(id, opts) {
|
|
45607
|
+
const taskId = id.trim();
|
|
45608
|
+
if (!taskId) throw new Error("AbleTime task id is required");
|
|
45609
|
+
const raw = await callAbleTimeTool("get_task", { id: taskId, task_id: taskId }, opts);
|
|
45610
|
+
const out = [];
|
|
45611
|
+
for (const blockerId of ableTimeBlockedByIdentifiers(raw)) {
|
|
45612
|
+
if (!looksLikeAbleTimeUlid(blockerId)) {
|
|
45613
|
+
out.push(blockerId);
|
|
45614
|
+
continue;
|
|
45615
|
+
}
|
|
45616
|
+
const parent = await callAbleTimeTool("get_task", { id: blockerId, task_id: blockerId }, opts);
|
|
45617
|
+
const ref = taskReference(parent);
|
|
45618
|
+
if (!ref || looksLikeAbleTimeUlid(ref)) {
|
|
45619
|
+
throw new Error(`AbleTime blocker has no reference: ${blockerId}`);
|
|
45620
|
+
}
|
|
45621
|
+
out.push(ref);
|
|
45622
|
+
}
|
|
45623
|
+
return out;
|
|
45624
|
+
}
|
|
45625
|
+
|
|
45626
|
+
// src/integrations/github-blocked.ts
|
|
45627
|
+
init_run();
|
|
45628
|
+
function githubBlockedByIdentifiers(payload) {
|
|
45629
|
+
const root = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
45630
|
+
const data = root?.data && typeof root.data === "object" && !Array.isArray(root.data) ? root.data : root;
|
|
45631
|
+
const repository = data?.repository && typeof data.repository === "object" ? data.repository : null;
|
|
45632
|
+
const issue = repository?.issue && typeof repository.issue === "object" ? repository.issue : null;
|
|
45633
|
+
const blocked = issue?.blockedBy && typeof issue.blockedBy === "object" ? issue.blockedBy : null;
|
|
45634
|
+
const nodes = Array.isArray(blocked?.nodes) ? blocked.nodes : Array.isArray(root?.blockedBy) ? root.blockedBy : [];
|
|
45635
|
+
const out = [];
|
|
45636
|
+
const seen = /* @__PURE__ */ new Set();
|
|
45637
|
+
for (const node of nodes) {
|
|
45638
|
+
const rec = node && typeof node === "object" && !Array.isArray(node) ? node : null;
|
|
45639
|
+
const number = Number(rec?.number ?? node);
|
|
45640
|
+
if (!Number.isFinite(number) || number <= 0) continue;
|
|
45641
|
+
const id = `#${number}`;
|
|
45642
|
+
const key = id.toLowerCase();
|
|
45643
|
+
if (seen.has(key)) continue;
|
|
45644
|
+
seen.add(key);
|
|
45645
|
+
out.push(id);
|
|
45646
|
+
}
|
|
45647
|
+
return out;
|
|
45648
|
+
}
|
|
45649
|
+
var GITHUB_BLOCKED_BY_QUERY = `
|
|
45650
|
+
query($owner: String!, $name: String!, $number: Int!) {
|
|
45651
|
+
repository(owner: $owner, name: $name) {
|
|
45652
|
+
issue(number: $number) {
|
|
45653
|
+
blockedBy(first: 25) { nodes { number } }
|
|
45654
|
+
}
|
|
45655
|
+
}
|
|
45656
|
+
}
|
|
45657
|
+
`;
|
|
45658
|
+
async function getGitHubIssueBlockedBy(id, opts) {
|
|
45659
|
+
const number = parseGitHubIssueNumber(id);
|
|
45660
|
+
const { cwd, slug } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
45661
|
+
const [owner, name] = (slug ?? "").split("/");
|
|
45662
|
+
if (!owner || !name) throw new Error("GitHub repo is not connected");
|
|
45663
|
+
const result = await gh(
|
|
45664
|
+
[
|
|
45665
|
+
"api",
|
|
45666
|
+
"graphql",
|
|
45667
|
+
"-f",
|
|
45668
|
+
`query=${GITHUB_BLOCKED_BY_QUERY}`,
|
|
45669
|
+
"-f",
|
|
45670
|
+
`owner=${owner}`,
|
|
45671
|
+
"-f",
|
|
45672
|
+
`name=${name}`,
|
|
45673
|
+
"-F",
|
|
45674
|
+
`number=${number}`
|
|
45675
|
+
],
|
|
45676
|
+
cwd,
|
|
45677
|
+
{ reject: false }
|
|
45678
|
+
);
|
|
45679
|
+
if (result.exitCode !== 0) {
|
|
45680
|
+
const detail = (result.stderr || result.stdout).trim() || "gh failed";
|
|
45681
|
+
throw new Error(`GitHub issue #${number} blocked-by: ${detail}`);
|
|
45682
|
+
}
|
|
45683
|
+
let parsed;
|
|
45684
|
+
try {
|
|
45685
|
+
parsed = JSON.parse(result.stdout);
|
|
45686
|
+
} catch {
|
|
45687
|
+
throw new Error(`GitHub issue #${number} blocked-by: gh returned non-JSON`);
|
|
45688
|
+
}
|
|
45689
|
+
const errors = parsed && typeof parsed === "object" && Array.isArray(parsed.errors) ? parsed.errors : [];
|
|
45690
|
+
if (errors.length > 0) {
|
|
45691
|
+
throw new Error(
|
|
45692
|
+
`GitHub issue #${number} blocked-by: ${errors.map((e) => e.message ?? "GitHub error").join("; ")}`
|
|
45693
|
+
);
|
|
45694
|
+
}
|
|
45695
|
+
const issue = parsed.data?.repository?.issue;
|
|
45696
|
+
if (!issue) throw new Error(`GitHub issue not found: #${number}`);
|
|
45697
|
+
return githubBlockedByIdentifiers(parsed);
|
|
45698
|
+
}
|
|
45699
|
+
|
|
45700
|
+
// src/integrations/linear-blocked.ts
|
|
45701
|
+
var ISSUE_BLOCKED_BY_QUERY = `
|
|
45702
|
+
query SideboardIssueBlockedBy($id: String!) {
|
|
45703
|
+
issue(id: $id) {
|
|
45704
|
+
identifier
|
|
45705
|
+
relations(first: 25) {
|
|
45706
|
+
nodes { id type relatedIssue { id identifier } }
|
|
45707
|
+
}
|
|
45708
|
+
inverseRelations(first: 25) {
|
|
45709
|
+
nodes { id type issue { id identifier } }
|
|
45710
|
+
}
|
|
45711
|
+
}
|
|
45712
|
+
}
|
|
45713
|
+
`;
|
|
45714
|
+
function linearBlockedByIdentifiers(relations) {
|
|
45715
|
+
const out = [];
|
|
45716
|
+
const seen = /* @__PURE__ */ new Set();
|
|
45717
|
+
for (const rel of relations) {
|
|
45718
|
+
const type = (rel.type ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
45719
|
+
if (type !== "blockedby" && type !== "blocked") continue;
|
|
45720
|
+
const id = (rel.issue?.identifier || rel.issue?.id || "").trim();
|
|
45721
|
+
const key = id.toLowerCase();
|
|
45722
|
+
if (!id || seen.has(key)) continue;
|
|
45723
|
+
seen.add(key);
|
|
45724
|
+
out.push(id);
|
|
45725
|
+
}
|
|
45726
|
+
return out;
|
|
45727
|
+
}
|
|
45728
|
+
function linearBlockedByRelations(issue) {
|
|
45729
|
+
const out = [];
|
|
45730
|
+
for (const rel of issue.relations?.nodes ?? []) {
|
|
45731
|
+
out.push({ type: String(rel.type ?? "related"), issue: rel.relatedIssue });
|
|
45732
|
+
}
|
|
45733
|
+
for (const rel of issue.inverseRelations?.nodes ?? []) {
|
|
45734
|
+
out.push({
|
|
45735
|
+
type: flipLinearRelationType(String(rel.type ?? "related")),
|
|
45736
|
+
issue: rel.issue
|
|
45737
|
+
});
|
|
45738
|
+
}
|
|
45739
|
+
return out;
|
|
45740
|
+
}
|
|
45741
|
+
async function getLinearIssueBlockedBy(id, opts) {
|
|
45742
|
+
const issueId = id.trim();
|
|
45743
|
+
if (!issueId) throw new Error("Linear issue id is required (uuid or ENG-123)");
|
|
45744
|
+
const json = await linearGraphql(
|
|
45745
|
+
ISSUE_BLOCKED_BY_QUERY,
|
|
45746
|
+
{ id: issueId },
|
|
45747
|
+
opts
|
|
45748
|
+
);
|
|
45749
|
+
if (!json.issue) throw new Error(`Linear issue not found: ${issueId}`);
|
|
45750
|
+
return linearBlockedByIdentifiers(linearBlockedByRelations(json.issue));
|
|
45751
|
+
}
|
|
45752
|
+
|
|
45753
|
+
// src/board/issue-blocked-sync.ts
|
|
45754
|
+
init_app_settings();
|
|
45755
|
+
init_thread_store();
|
|
45756
|
+
init_chat_tabs();
|
|
45757
|
+
function sourceTicketRef(thread) {
|
|
45758
|
+
if (thread.sourceType !== "ticket") return null;
|
|
45759
|
+
const ref = (thread.sourceRef ?? "").trim();
|
|
45760
|
+
return ref || null;
|
|
45761
|
+
}
|
|
45762
|
+
function sameIssueBlockedBy(current, next) {
|
|
45763
|
+
const left = new Set(
|
|
45764
|
+
(current ?? []).map((id) => id.trim().toLowerCase()).filter(Boolean)
|
|
45765
|
+
);
|
|
45766
|
+
const right = [...new Set(next.map((id) => id.trim().toLowerCase()).filter(Boolean))];
|
|
45767
|
+
if (left.size !== right.length) return false;
|
|
45768
|
+
return right.every((id) => left.has(id));
|
|
45769
|
+
}
|
|
45770
|
+
var TRACKER_KEY = /^[A-Za-z][A-Za-z0-9]+-\d+$/;
|
|
45771
|
+
function isGitHubIssueRef(ref) {
|
|
45772
|
+
return /^#?\d+$/.test(ref) || /^gh-\d+$/i.test(ref) || /github\.com\/[^/]+\/[^/]+\/issues\/\d+/i.test(ref);
|
|
45773
|
+
}
|
|
45774
|
+
function trackerForSourceTicket(ref, preferred, connected) {
|
|
45775
|
+
const trimmed = ref.trim();
|
|
45776
|
+
if (!trimmed) return null;
|
|
45777
|
+
if (isGitHubIssueRef(trimmed)) return "github";
|
|
45778
|
+
if (TRACKER_KEY.test(trimmed)) {
|
|
45779
|
+
if (preferred === "abletime" && connected.abletime) return "abletime";
|
|
45780
|
+
if (connected.linear) return "linear";
|
|
45781
|
+
if (connected.abletime) return "abletime";
|
|
45782
|
+
return null;
|
|
45783
|
+
}
|
|
45784
|
+
if (preferred === "linear" && connected.linear) return "linear";
|
|
45785
|
+
if (preferred === "abletime" && connected.abletime) return "abletime";
|
|
45786
|
+
if (preferred === "github") return "github";
|
|
45787
|
+
return null;
|
|
45788
|
+
}
|
|
45789
|
+
function defaultIssueBlockedLookup() {
|
|
45790
|
+
return {
|
|
45791
|
+
preferredSource: () => resolveEffectiveIssueSource(),
|
|
45792
|
+
linearConnected: () => isLinearConnected(),
|
|
45793
|
+
abletimeConnected: () => isAbleTimeConnected(),
|
|
45794
|
+
linear: (id) => getLinearIssueBlockedBy(id),
|
|
45795
|
+
github: (id, repoPath) => getGitHubIssueBlockedBy(id, { repoPath }),
|
|
45796
|
+
abletime: (id) => getAbleTimeIssueBlockedBy(id)
|
|
45797
|
+
};
|
|
45798
|
+
}
|
|
45799
|
+
async function lookupIssueBlockedBy(thread, lookup = defaultIssueBlockedLookup()) {
|
|
45800
|
+
const ref = sourceTicketRef(thread);
|
|
45801
|
+
if (!ref) return null;
|
|
45802
|
+
const tracker = trackerForSourceTicket(ref, lookup.preferredSource(), {
|
|
45803
|
+
linear: lookup.linearConnected(),
|
|
45804
|
+
abletime: lookup.abletimeConnected()
|
|
45805
|
+
});
|
|
45806
|
+
if (!tracker) return null;
|
|
45807
|
+
try {
|
|
45808
|
+
if (tracker === "linear") return await lookup.linear(ref);
|
|
45809
|
+
if (tracker === "abletime") return await lookup.abletime(ref);
|
|
45810
|
+
const repo = (thread.repoPath || thread.worktreePath || "").trim();
|
|
45811
|
+
return await lookup.github(ref, repo);
|
|
45812
|
+
} catch {
|
|
45813
|
+
return null;
|
|
45814
|
+
}
|
|
45815
|
+
}
|
|
45816
|
+
async function refreshWorktreeIssueBlocked(threadRef) {
|
|
45817
|
+
const thread = readThread(threadRef) ?? findThreadByRef(threadRef);
|
|
45818
|
+
if (!thread) throw new Error(`Thread not found: ${threadRef}`);
|
|
45819
|
+
const ids = await lookupIssueBlockedBy(thread);
|
|
45820
|
+
if (ids == null) return null;
|
|
45821
|
+
const siblings = thread.worktreePath ? threadsSharingWorktree(thread.worktreePath) : [];
|
|
45822
|
+
const targets = siblings.length > 0 ? siblings : [thread];
|
|
45823
|
+
for (const t of targets) {
|
|
45824
|
+
if (sameIssueBlockedBy(t.issueBlockedBy, ids)) continue;
|
|
45825
|
+
updateThread(t.id, { issueBlockedBy: ids });
|
|
45826
|
+
}
|
|
45827
|
+
return ids;
|
|
45828
|
+
}
|
|
45829
|
+
|
|
45830
|
+
// src/index.ts
|
|
45514
45831
|
init_config();
|
|
45515
45832
|
|
|
45516
45833
|
// src/brightsy/api.ts
|
|
@@ -48268,6 +48585,7 @@ async function pollSlackOutboundWatches(opts) {
|
|
|
48268
48585
|
recordSlackOutboundWatch,
|
|
48269
48586
|
refreshBrightsyAccessToken,
|
|
48270
48587
|
refreshGitHubAuth,
|
|
48588
|
+
refreshWorktreeIssueBlocked,
|
|
48271
48589
|
registerBrightsyOAuthClient,
|
|
48272
48590
|
registerPackagedUserMcpClients,
|
|
48273
48591
|
releaseCaffeinateHoldForThread,
|