claude-teammate 0.1.147 → 0.1.148

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-teammate",
3
- "version": "0.1.147",
3
+ "version": "0.1.148",
4
4
  "description": "CLI bootstrapper for Claude Teammate.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -75,11 +75,17 @@ export function createGitHubClient(config) {
75
75
 
76
76
  async listIssues(repoUrl, options = {}) {
77
77
  const repo = parseGitHubRepoUrl(repoUrl);
78
- const url = new URL(`https://api.github.com/repos/${repo.owner}/${repo.name}/issues`);
79
- url.searchParams.set("state", "open");
80
- url.searchParams.set("per_page", "100");
81
-
82
- const payload = await requestGitHub(url, config, { method: "GET" }, repo);
78
+ const payload = await requestGitHubPaginated(
79
+ `https://api.github.com/repos/${repo.owner}/${repo.name}/issues`,
80
+ config,
81
+ {
82
+ searchParams: {
83
+ state: "open",
84
+ per_page: "100"
85
+ }
86
+ },
87
+ repo
88
+ );
83
89
  const issues = Array.isArray(payload)
84
90
  ? payload
85
91
  .filter((issue) => !issue.pull_request)
@@ -91,11 +97,17 @@ export function createGitHubClient(config) {
91
97
 
92
98
  async listPullRequests(repoUrl, options = {}) {
93
99
  const repo = parseGitHubRepoUrl(repoUrl);
94
- const url = new URL(`https://api.github.com/repos/${repo.owner}/${repo.name}/pulls`);
95
- url.searchParams.set("state", "open");
96
- url.searchParams.set("per_page", "100");
97
-
98
- const payload = await requestGitHub(url, config, { method: "GET" }, repo);
100
+ const payload = await requestGitHubPaginated(
101
+ `https://api.github.com/repos/${repo.owner}/${repo.name}/pulls`,
102
+ config,
103
+ {
104
+ searchParams: {
105
+ state: "open",
106
+ per_page: "100"
107
+ }
108
+ },
109
+ repo
110
+ );
99
111
  const pullRequests = Array.isArray(payload)
100
112
  ? payload.map((pullRequest) => mapGitHubPullRequestSummary(pullRequest))
101
113
  : [];
@@ -299,11 +311,18 @@ export function createGitHubClient(config) {
299
311
 
300
312
  async listPrsNeedingReview() {
301
313
  const query = "is:pr is:open user-review-requested:@me -label:AI-reviewed";
302
- const url = new URL("https://api.github.com/search/issues");
303
- url.searchParams.set("q", query);
304
- url.searchParams.set("per_page", "10");
305
-
306
- const payload = await requestGitHub(url, config, { method: "GET" });
314
+ const payload = await requestGitHubPaginated(
315
+ "https://api.github.com/search/issues",
316
+ config,
317
+ {
318
+ searchParams: {
319
+ q: query,
320
+ per_page: "100"
321
+ }
322
+ },
323
+ null,
324
+ "items"
325
+ );
307
326
  return Array.isArray(payload.items)
308
327
  ? payload.items.map((item) => ({
309
328
  repoUrl: mapRepositoryApiUrlToHtmlUrl(item.repository_url),
@@ -600,11 +619,18 @@ function mapGitHubLabels(labels) {
600
619
 
601
620
  async function searchGitHubTrackedItems({ config, type, botLogin }) {
602
621
  const query = buildTrackedSearchQuery(type, botLogin);
603
- const url = new URL("https://api.github.com/search/issues");
604
- url.searchParams.set("q", query);
605
- url.searchParams.set("per_page", "100");
606
-
607
- const payload = await requestGitHub(url, config, { method: "GET" });
622
+ const payload = await requestGitHubPaginated(
623
+ "https://api.github.com/search/issues",
624
+ config,
625
+ {
626
+ searchParams: {
627
+ q: query,
628
+ per_page: "100"
629
+ }
630
+ },
631
+ null,
632
+ "items"
633
+ );
608
634
  const items = Array.isArray(payload.items) ? payload.items : [];
609
635
 
610
636
  return items
@@ -701,9 +727,9 @@ function buildReviewComments(suggestions) {
701
727
  }
702
728
 
703
729
  async function requestGitHub(url, config, init = {}, repo = null) {
704
- const targetUrl = typeof url === "string" ? new URL(url) : url;
730
+ const targetUrl = buildGitHubUrl(url, init.searchParams);
705
731
  const method = String(init.method || "GET").toUpperCase();
706
- const response = await fetchGitHubRaw(url, config, init);
732
+ const response = await fetchGitHubRaw(targetUrl, config, init);
707
733
 
708
734
  if (!response.ok) {
709
735
  const body = await response.text();
@@ -720,6 +746,49 @@ async function requestGitHub(url, config, init = {}, repo = null) {
720
746
  return response.json();
721
747
  }
722
748
 
749
+ async function requestGitHubPaginated(url, config, init = {}, repo = null, itemsKey = null) {
750
+ const results = [];
751
+ let page = 1;
752
+
753
+ while (true) {
754
+ const targetUrl = buildGitHubUrl(url, {
755
+ ...(init.searchParams || {}),
756
+ per_page: init.searchParams?.per_page || "100",
757
+ page: String(page)
758
+ });
759
+ const payload = await requestGitHub(targetUrl, config, init, repo);
760
+ const items = itemsKey ? payload?.[itemsKey] : payload;
761
+ const batch = Array.isArray(items) ? items : [];
762
+ results.push(...batch);
763
+
764
+ if (batch.length < 100) {
765
+ if (!itemsKey) {
766
+ return results;
767
+ }
768
+ return {
769
+ ...(payload && typeof payload === "object" ? payload : {}),
770
+ [itemsKey]: results
771
+ };
772
+ }
773
+
774
+ page += 1;
775
+ }
776
+ }
777
+
778
+ function buildGitHubUrl(url, searchParams = null) {
779
+ const targetUrl = typeof url === "string" ? new URL(url) : new URL(String(url));
780
+
781
+ if (searchParams) {
782
+ for (const [key, value] of Object.entries(searchParams)) {
783
+ if (value !== undefined && value !== null && value !== "") {
784
+ targetUrl.searchParams.set(key, value);
785
+ }
786
+ }
787
+ }
788
+
789
+ return targetUrl;
790
+ }
791
+
723
792
  async function requestGitHubGraphql(config, query, variables = {}) {
724
793
  const response = await fetch("https://api.github.com/graphql", {
725
794
  method: "POST",
@@ -68,7 +68,8 @@ export function createGitLabClient(config) {
68
68
 
69
69
  async listIssues(repoUrl, options = {}) {
70
70
  const repo = parseGitLabRepoUrl(repoUrl);
71
- const payload = await requestGitLabProject(config, repo, "/issues", {
71
+ const payload = await requestGitLabPaginated(config, projectPath(repo, "/issues"), {
72
+ baseUrl: `${repo.origin}/`,
72
73
  searchParams: {
73
74
  state: "opened",
74
75
  per_page: "100"
@@ -82,7 +83,8 @@ export function createGitLabClient(config) {
82
83
 
83
84
  async listPullRequests(repoUrl, options = {}) {
84
85
  const repo = parseGitLabRepoUrl(repoUrl);
85
- const payload = await requestGitLabProject(config, repo, "/merge_requests", {
86
+ const payload = await requestGitLabPaginated(config, projectPath(repo, "/merge_requests"), {
87
+ baseUrl: `${repo.origin}/`,
86
88
  searchParams: {
87
89
  state: "opened",
88
90
  per_page: "100"
@@ -292,7 +294,7 @@ export function createGitLabClient(config) {
292
294
  if (!currentUser.login) {
293
295
  continue;
294
296
  }
295
- const payload = await requestGitLab(config, "/api/v4/merge_requests", {
297
+ const payload = await requestGitLabPaginated(config, "/api/v4/merge_requests", {
296
298
  baseUrl,
297
299
  searchParams: {
298
300
  state: "opened",
@@ -787,15 +789,19 @@ async function listGitLabTrackedItems({ config, repoUrls = [], type, authorLogin
787
789
  const seen = new Set();
788
790
 
789
791
  for (const baseUrl of baseUrls) {
790
- const payload = await requestGitLab(config, type === "pr" ? "/api/v4/merge_requests" : "/api/v4/issues", {
791
- baseUrl,
792
- searchParams: {
793
- state: "opened",
794
- per_page: "100",
795
- scope: "all",
796
- author_username: authorLogin
792
+ const payload = await requestGitLabPaginated(
793
+ config,
794
+ type === "pr" ? "/api/v4/merge_requests" : "/api/v4/issues",
795
+ {
796
+ baseUrl,
797
+ searchParams: {
798
+ state: "opened",
799
+ per_page: "100",
800
+ scope: "all",
801
+ author_username: authorLogin
802
+ }
797
803
  }
798
- });
804
+ );
799
805
  const items = Array.isArray(payload) ? payload : [];
800
806
  for (const item of items) {
801
807
  const key = `${item.web_url || ""}|${item.iid || ""}`;
@@ -891,6 +897,30 @@ async function requestGitLab(config, pathOrUrl, init = {}) {
891
897
  return response.json();
892
898
  }
893
899
 
900
+ async function requestGitLabPaginated(config, pathOrUrl, init = {}) {
901
+ const results = [];
902
+ let page = 1;
903
+
904
+ while (true) {
905
+ const payload = await requestGitLab(config, pathOrUrl, {
906
+ ...init,
907
+ searchParams: {
908
+ ...(init.searchParams || {}),
909
+ per_page: init.searchParams?.per_page || "100",
910
+ page: String(page)
911
+ }
912
+ });
913
+ const batch = Array.isArray(payload) ? payload : [];
914
+ results.push(...batch);
915
+
916
+ if (batch.length < 100) {
917
+ return results;
918
+ }
919
+
920
+ page += 1;
921
+ }
922
+ }
923
+
894
924
  function determineGitLabBaseUrl(config) {
895
925
  const raw = String(config.GITLAB_BASE_URL || "").trim();
896
926
  if (raw) {
package/src/jira.js CHANGED
@@ -14,30 +14,44 @@ export function createJiraClient(config) {
14
14
 
15
15
  return {
16
16
  async fetchAssignedIssues() {
17
- const url = new URL(`${baseUrl}${SEARCH_PATH}`);
18
17
  const jql = buildAssignedIssuesJql(config.JIRA_BOT_EMAIL);
19
- url.searchParams.set("jql", jql);
20
- url.searchParams.set(
21
- "fields",
22
- [
23
- "summary",
24
- "status",
25
- "updated",
26
- "created",
27
- "project",
28
- "issuetype",
29
- "assignee",
30
- "labels",
31
- "parent"
32
- ].join(",")
33
- );
34
- url.searchParams.set("maxResults", "50");
35
-
36
- const payload = await requestJson(url, auth);
37
- const issues = Array.isArray(payload.issues) ? payload.issues.map((issue) => mapIssue(issue, baseUrl)) : [];
18
+ const issues = [];
19
+ let startAt = 0;
20
+ let total = 0;
21
+
22
+ do {
23
+ const url = new URL(`${baseUrl}${SEARCH_PATH}`);
24
+ url.searchParams.set("jql", jql);
25
+ url.searchParams.set(
26
+ "fields",
27
+ [
28
+ "summary",
29
+ "status",
30
+ "updated",
31
+ "created",
32
+ "project",
33
+ "issuetype",
34
+ "assignee",
35
+ "labels",
36
+ "parent"
37
+ ].join(",")
38
+ );
39
+ url.searchParams.set("maxResults", "100");
40
+ url.searchParams.set("startAt", String(startAt));
41
+
42
+ const payload = await requestJson(url, auth);
43
+ const batch = Array.isArray(payload.issues) ? payload.issues.map((issue) => mapIssue(issue, baseUrl)) : [];
44
+ total = Number.isInteger(payload.total) && payload.total > 0 ? payload.total : batch.length;
45
+ issues.push(...batch);
46
+ startAt += batch.length;
47
+
48
+ if (batch.length === 0) {
49
+ break;
50
+ }
51
+ } while (startAt < total);
38
52
 
39
53
  return {
40
- total: Number.isInteger(payload.total) && payload.total > 0 ? payload.total : issues.length,
54
+ total: total > 0 ? total : issues.length,
41
55
  issues,
42
56
  jql
43
57
  };
@@ -191,7 +205,7 @@ export function createJiraClient(config) {
191
205
 
192
206
  export function buildAssignedIssuesJql(botEmail) {
193
207
  const escapedEmail = botEmail.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"');
194
- return `created >= -30d AND assignee = "${escapedEmail}" ORDER BY created ASC`;
208
+ return `created >= -30d AND assignee = "${escapedEmail}" AND statusCategory != Done ORDER BY created ASC`;
195
209
  }
196
210
 
197
211
  export function isBotAuthor(author, botUser, configuredEmail) {