@sideboard-ai/core 0.1.183 → 0.1.185

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/index.js CHANGED
@@ -238,7 +238,7 @@ import {
238
238
  worktreeCleanupSettings,
239
239
  wrapReviewSkillMarkdown,
240
240
  writeWorktreeFile
241
- } from "./chunk-26N6LKLE.js";
241
+ } from "./chunk-G4VAMUCL.js";
242
242
  import {
243
243
  LEGACY_PLAN_FILE_REL,
244
244
  PLAN_FILE_NAME,
@@ -344,7 +344,7 @@ import {
344
344
  withEventParentId,
345
345
  withEventsParentId,
346
346
  writeInjectedMcpConfig
347
- } from "./chunk-VA76U3BU.js";
347
+ } from "./chunk-5FTIOINZ.js";
348
348
  import {
349
349
  CLOUD_COORDINATOR_BUSY_REPLY,
350
350
  CLOUD_COORDINATOR_STOPPED_REPLY,
@@ -387,7 +387,7 @@ import {
387
387
  stageBuffersAsAttachments,
388
388
  syncWorkspacesFromThreads,
389
389
  takenTeamSlugsForOrchestration
390
- } from "./chunk-JFDCHOU7.js";
390
+ } from "./chunk-VIODP5PT.js";
391
391
  import {
392
392
  FAMOUS_SOCCER_TEAMS,
393
393
  GITHUB_PR_BODY_MAX_CHARS,
@@ -1518,12 +1518,27 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1518
1518
  function assigneeFilterKey(assignee) {
1519
1519
  return (assignee ?? "").trim();
1520
1520
  }
1521
+ function applyLinearIssueQueryFilter(filter, query) {
1522
+ const term = query?.trim();
1523
+ if (!term) return;
1524
+ const or = [
1525
+ { title: { containsIgnoreCase: term } },
1526
+ { description: { containsIgnoreCase: term } }
1527
+ ];
1528
+ const identifierMatch = term.match(/^[A-Za-z][\w]*-(\d+)$/);
1529
+ const asNumber = identifierMatch ? Number(identifierMatch[1]) : /^\d+$/.test(term) ? Number(term) : NaN;
1530
+ if (Number.isFinite(asNumber) && asNumber > 0) {
1531
+ or.push({ number: { eq: asNumber } });
1532
+ }
1533
+ filter.or = or;
1534
+ }
1521
1535
  function buildLinearIssueFilter(input) {
1522
1536
  const filter = {
1523
1537
  state: { type: { nin: ["completed", "canceled"] } }
1524
1538
  };
1525
1539
  const since = input?.updatedSince?.trim();
1526
1540
  if (since) filter.updatedAt = { gte: since };
1541
+ applyLinearIssueQueryFilter(filter, input?.query);
1527
1542
  const raw = assigneeFilterKey(input?.assignee) || "me";
1528
1543
  const key = raw.toLowerCase();
1529
1544
  if (key === "all" || key === "*") return filter;
@@ -1589,6 +1604,11 @@ async function listLinearIssuesFiltered(opts) {
1589
1604
  async function listLinearCommentsSince(opts) {
1590
1605
  const first = Math.max(1, Math.min(250, opts.limit ?? 200));
1591
1606
  const query = opts.query?.trim() ?? "";
1607
+ const restrictToIssues = opts.issueIdentifiers != null;
1608
+ const allowed = new Set(
1609
+ (opts.issueIdentifiers ?? []).map((id) => id.trim()).filter(Boolean)
1610
+ );
1611
+ if (restrictToIssues && allowed.size === 0) return [];
1592
1612
  const assignee = assigneeFilterKey(opts.assignee) || (query ? "all" : "me");
1593
1613
  const json = await linearGraphql(
1594
1614
  COMMENTS_SINCE_QUERY,
@@ -1596,7 +1616,7 @@ async function listLinearCommentsSince(opts) {
1596
1616
  first,
1597
1617
  filter: {
1598
1618
  createdAt: { gte: opts.since },
1599
- issue: buildLinearIssueFilter({ assignee })
1619
+ issue: buildLinearIssueFilter({ assignee, query: query || void 0 })
1600
1620
  }
1601
1621
  },
1602
1622
  opts
@@ -1604,6 +1624,7 @@ async function listLinearCommentsSince(opts) {
1604
1624
  const out = [];
1605
1625
  for (const node of json.comments?.nodes ?? []) {
1606
1626
  const identifier = String(node.issue?.identifier ?? "").trim();
1627
+ if (restrictToIssues && !allowed.has(identifier)) continue;
1607
1628
  const body = previewIssueCommentBody(String(node.body ?? ""));
1608
1629
  if (!identifier && !body) continue;
1609
1630
  out.push({
@@ -1833,10 +1854,32 @@ function issueNumberFromApiUrl(url) {
1833
1854
  const number = Number(match[1]);
1834
1855
  return Number.isFinite(number) && number > 0 ? number : null;
1835
1856
  }
1857
+ var GITHUB_ISSUE_COMMENTS_PAGE_SIZE = 100;
1858
+ var GITHUB_ISSUE_COMMENTS_MAX_PAGES = 5;
1859
+ function parseGitHubIssueCommentNode(item, allowed) {
1860
+ const rec = item && typeof item === "object" ? item : null;
1861
+ if (!rec) return null;
1862
+ const issueUrl = typeof rec.issue_url === "string" ? rec.issue_url : "";
1863
+ const number = issueNumberFromApiUrl(issueUrl);
1864
+ if (number == null) return null;
1865
+ if (allowed && !allowed.has(number)) return null;
1866
+ const body = previewIssueCommentBody(typeof rec.body === "string" ? rec.body : "");
1867
+ const user = rec.user && typeof rec.user === "object" ? String(rec.user.login ?? "").trim() : "";
1868
+ const createdAt = typeof rec.created_at === "string" ? rec.created_at : typeof rec.createdAt === "string" ? rec.createdAt : void 0;
1869
+ const url = typeof rec.html_url === "string" ? rec.html_url : void 0;
1870
+ return {
1871
+ identifier: `#${number}`,
1872
+ ...user ? { author: user } : {},
1873
+ ...createdAt ? { createdAt } : {},
1874
+ body,
1875
+ ...url ? { url } : {}
1876
+ };
1877
+ }
1836
1878
  async function listGitHubIssueCommentsSince(opts) {
1837
1879
  const limit = Math.max(1, Math.min(250, opts.limit ?? 200));
1838
1880
  const { cwd, slug } = await resolveGitHubIssueRepo(opts.repoPath);
1839
1881
  if (!slug) return [];
1882
+ const restrictToIssues = opts.issueIdentifiers != null;
1840
1883
  const allowed = new Set(
1841
1884
  (opts.issueIdentifiers ?? []).map((id) => {
1842
1885
  try {
@@ -1846,36 +1889,27 @@ async function listGitHubIssueCommentsSince(opts) {
1846
1889
  }
1847
1890
  }).filter((n) => n != null)
1848
1891
  );
1849
- const path2 = `repos/${slug}/issues/comments?since=${encodeURIComponent(opts.since)}&per_page=${limit + 1}`;
1850
- const result = await gh(["api", path2], cwd, { reject: false });
1851
- if (result.exitCode !== 0 || !result.stdout.trim()) return [];
1852
- let parsed;
1853
- try {
1854
- parsed = JSON.parse(result.stdout);
1855
- } catch {
1856
- return [];
1857
- }
1858
- if (!Array.isArray(parsed)) return [];
1892
+ if (restrictToIssues && allowed.size === 0) return [];
1893
+ const pageSize = Math.min(GITHUB_ISSUE_COMMENTS_PAGE_SIZE, Math.max(1, limit));
1859
1894
  const out = [];
1860
- for (const item of parsed) {
1861
- const rec = item && typeof item === "object" ? item : null;
1862
- if (!rec) continue;
1863
- const issueUrl = typeof rec.issue_url === "string" ? rec.issue_url : "";
1864
- const number = issueNumberFromApiUrl(issueUrl);
1865
- if (number == null) continue;
1866
- if (allowed.size > 0 && !allowed.has(number)) continue;
1867
- const body = previewIssueCommentBody(typeof rec.body === "string" ? rec.body : "");
1868
- const user = rec.user && typeof rec.user === "object" ? String(rec.user.login ?? "").trim() : "";
1869
- const createdAt = typeof rec.created_at === "string" ? rec.created_at : typeof rec.createdAt === "string" ? rec.createdAt : void 0;
1870
- const url = typeof rec.html_url === "string" ? rec.html_url : void 0;
1871
- out.push({
1872
- identifier: `#${number}`,
1873
- ...user ? { author: user } : {},
1874
- ...createdAt ? { createdAt } : {},
1875
- body,
1876
- ...url ? { url } : {}
1877
- });
1878
- if (out.length >= limit) break;
1895
+ for (let page = 1; page <= GITHUB_ISSUE_COMMENTS_MAX_PAGES; page++) {
1896
+ const path2 = `repos/${slug}/issues/comments?since=${encodeURIComponent(opts.since)}&sort=created&direction=desc&per_page=${pageSize}&page=${page}`;
1897
+ const result = await gh(["api", path2], cwd, { reject: false });
1898
+ if (result.exitCode !== 0 || !result.stdout.trim()) break;
1899
+ let parsed;
1900
+ try {
1901
+ parsed = JSON.parse(result.stdout);
1902
+ } catch {
1903
+ break;
1904
+ }
1905
+ if (!Array.isArray(parsed) || parsed.length === 0) break;
1906
+ for (const item of parsed) {
1907
+ const comment = parseGitHubIssueCommentNode(item, restrictToIssues ? allowed : null);
1908
+ if (!comment) continue;
1909
+ out.push(comment);
1910
+ if (out.length >= limit) return out;
1911
+ }
1912
+ if (parsed.length < pageSize) break;
1879
1913
  }
1880
1914
  return out;
1881
1915
  }
@@ -2081,20 +2115,19 @@ async function listIssues(repoPath, opts) {
2081
2115
  const assignee = opts?.assignee?.trim() || void 0;
2082
2116
  const since = opts?.updatedSince ? parseIssueSince(opts.updatedSince) : void 0;
2083
2117
  if (source === "linear") {
2084
- const [listed, comments2] = await Promise.all([
2085
- listLinearIssuesFiltered({
2086
- assignee,
2087
- query,
2088
- limit: opts?.limit,
2089
- updatedSince: since
2090
- }),
2091
- since ? listLinearCommentsSince({
2092
- since,
2093
- assignee,
2094
- query,
2095
- limit: opts?.limit
2096
- }) : Promise.resolve(void 0)
2097
- ]);
2118
+ const listed = await listLinearIssuesFiltered({
2119
+ assignee,
2120
+ query,
2121
+ limit: opts?.limit,
2122
+ updatedSince: since
2123
+ });
2124
+ const comments2 = since ? await listLinearCommentsSince({
2125
+ since,
2126
+ assignee,
2127
+ query,
2128
+ issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
2129
+ limit: opts?.limit
2130
+ }) : void 0;
2098
2131
  return {
2099
2132
  source,
2100
2133
  preferredSource,
@@ -3536,7 +3569,7 @@ function registerAbleTimeTools(server) {
3536
3569
  );
3537
3570
  server.tool(
3538
3571
  "abletime_comment",
3539
- "Add a markdown comment on an AbleTime task (id or CRM-232). When reviewing a PR or ticket, show the draft in chat and ask_user (Post this review / Keep it in chat) first \u2014 the author sees this immediately.",
3572
+ "Add a markdown comment on an AbleTime task (id or CRM-232). When reviewing a PR or ticket, show the draft in chat and wait until they type a next step \u2014 do not ask_user after the review. The author sees this immediately.",
3540
3573
  { id: z4.string(), body: z4.string() },
3541
3574
  async (args) => {
3542
3575
  try {
@@ -3548,7 +3581,7 @@ function registerAbleTimeTools(server) {
3548
3581
  );
3549
3582
  server.tool(
3550
3583
  "abletime_update_task",
3551
- "Update an AbleTime task (id or CRM-232). Pass title, description, and/or state. When reviewing a PR or ticket, ask_user (Post this review / Keep it in chat) before changing the task \u2014 the author is notified.",
3584
+ "Update an AbleTime task (id or CRM-232). Pass title, description, and/or state. 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.",
3552
3585
  {
3553
3586
  id: z4.string(),
3554
3587
  title: z4.string().optional(),
@@ -3677,7 +3710,7 @@ function registerGithubIssueTools(server) {
3677
3710
  );
3678
3711
  server.tool(
3679
3712
  "github_comment",
3680
- "Add a markdown comment on a GitHub issue (#123 or URL). Uses Account gh. When reviewing a PR or ticket, show the draft in chat and ask_user (Post this review / Keep it in chat) first \u2014 the author sees this immediately.",
3713
+ "Add a markdown comment on a GitHub issue (#123 or URL). Uses Account gh. When reviewing a PR or ticket, show the draft in chat and wait until they type a next step \u2014 do not ask_user after the review. The author sees this immediately.",
3681
3714
  { id: z5.string(), body: z5.string(), repoPath: repoPathSchema },
3682
3715
  async ({ id, body, repoPath }) => {
3683
3716
  try {
@@ -3689,7 +3722,7 @@ function registerGithubIssueTools(server) {
3689
3722
  );
3690
3723
  server.tool(
3691
3724
  "github_update_issue",
3692
- "Update a GitHub issue (#123). Pass title, body, and/or state (open|closed). When reviewing a PR or ticket, ask_user (Post this review / Keep it in chat) before changing the issue \u2014 the author is notified.",
3725
+ "Update a GitHub issue (#123). Pass title, body, and/or state (open|closed). 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.",
3693
3726
  {
3694
3727
  id: z5.string(),
3695
3728
  title: z5.string().optional(),
@@ -3762,20 +3795,19 @@ function registerLinearTools(server) {
3762
3795
  try {
3763
3796
  const page = clampMcpIssueLimit(limit);
3764
3797
  const since = updatedSince ? parseIssueSince(updatedSince) : void 0;
3765
- const [listed, comments] = await Promise.all([
3766
- listLinearIssuesFiltered({
3767
- query,
3768
- assignee,
3769
- limit: page + 1,
3770
- updatedSince: since
3771
- }),
3772
- since ? listLinearCommentsSince({
3773
- since,
3774
- assignee,
3775
- query,
3776
- limit: page + 1
3777
- }) : Promise.resolve(void 0)
3778
- ]);
3798
+ const listed = await listLinearIssuesFiltered({
3799
+ query,
3800
+ assignee,
3801
+ limit: page + 1,
3802
+ updatedSince: since
3803
+ });
3804
+ const comments = since ? await listLinearCommentsSince({
3805
+ since,
3806
+ assignee,
3807
+ query,
3808
+ issueIdentifiers: query ? listed.issues.map((issue) => issue.identifier) : void 0,
3809
+ limit: page + 1
3810
+ }) : void 0;
3779
3811
  return mcpJson(
3780
3812
  formatListedIssuesForMcp(
3781
3813
  {
@@ -3827,7 +3859,7 @@ function registerLinearTools(server) {
3827
3859
  );
3828
3860
  server.tool(
3829
3861
  "linear_update_issue",
3830
- "Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, and/or priority. When reviewing a PR or ticket, ask_user (Post this review / Keep it in chat) before changing the ticket \u2014 the author is notified.",
3862
+ "Update a Linear issue (uuid or ENG-123). Pass title, description, state, assignee, and/or priority. 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.",
3831
3863
  {
3832
3864
  id: z6.string(),
3833
3865
  title: z6.string().optional(),
@@ -3846,7 +3878,7 @@ function registerLinearTools(server) {
3846
3878
  );
3847
3879
  server.tool(
3848
3880
  "linear_comment",
3849
- "Add a markdown comment on a Linear issue (uuid or ENG-123). When reviewing a PR or ticket, show the draft in chat and ask_user (Post this review / Keep it in chat) first \u2014 the author sees this immediately.",
3881
+ "Add a markdown comment on a Linear issue (uuid or ENG-123). When reviewing a PR or ticket, show the draft in chat and wait until they type a next step \u2014 do not ask_user after the review. The author sees this immediately.",
3850
3882
  {
3851
3883
  id: z6.string(),
3852
3884
  body: z6.string()
@@ -5229,7 +5261,7 @@ async function startMcpServer() {
5229
5261
  );
5230
5262
  server.tool(
5231
5263
  "request_review",
5232
- 'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .claude/skills/review/SKILL.md when present (else copies .sideboard/review.md into .context/review.md, or seeds that file from the stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information in that chat. Do not comment on or update the PR or ticket until the user confirms (ask_user: Post this review / Keep it in chat) \u2014 they work through the feedback in chat first. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review tab id.',
5264
+ 'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .claude/skills/review/SKILL.md when present (else copies .sideboard/review.md into .context/review.md, or seeds that file from the stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information in that chat. The review stays there so the user can read it and type next steps \u2014 do not comment on or update the PR or ticket, and do not ask_user after the review, until they ask. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review tab id.',
5233
5265
  { ref: z9.string().describe("Worktree thread id/ref to review") },
5234
5266
  async ({ ref }) => {
5235
5267
  try {