diff --git a/sdk/typescript/src/linear.ts b/sdk/typescript/src/linear.ts
index fd14cc05e..f9386e206 100644
--- a/sdk/typescript/src/linear.ts
+++ b/sdk/typescript/src/linear.ts
@@ -83,9 +83,12 @@ export async function importLinearIssues(options: {
);
}
- const page = await projects.nodes[0]!.issues({ first: 50, filter });
- while (page.pageInfo.hasNextPage) await page.fetchNext();
- issues.push(...page.nodes);
+ let page = await projects.nodes[0]!.issues({ first: 50, filter });
+ while (true) {
+ issues.push(...page.nodes);
+ if (!page.pageInfo.hasNextPage) break;
+ page = await page.fetchNext();
+ }
if (issues.length === 0) {
throw new CodexSecurityError(
`No open Linear issues matched project "${options.project}" and its filter.`,
@@ -114,8 +117,13 @@ export async function importLinearIssues(options: {
const imports: ImportedIssue[] = [];
for (const issue of issues) {
- const comments = await issue.comments({ first: 50 });
- while (comments.pageInfo.hasNextPage) await comments.fetchNext();
+ let comments = await issue.comments({ first: 50 });
+ const commentNodes = [];
+ while (true) {
+ commentNodes.push(...comments.nodes);
+ if (!comments.pageInfo.hasNextPage) break;
+ comments = await comments.fetchNext();
+ }
imports.push({
source: "linear",
id: issue.identifier,
@@ -123,7 +131,7 @@ export async function importLinearIssues(options: {
text: [
`Title: ${issue.title}`,
`\n${issue.description ?? ""}\n`,
- ...comments.nodes.map(
+ ...commentNodes.map(
({ url, body }) => `\nURL: ${url}\n\n${body}\n`,
),
].join("\n\n"),
diff --git a/sdk/typescript/tests-ts/cli-skills.test.ts b/sdk/typescript/tests-ts/cli-skills.test.ts
index 252f8d15f..bdb2f1235 100644
--- a/sdk/typescript/tests-ts/cli-skills.test.ts
+++ b/sdk/typescript/tests-ts/cli-skills.test.ts
@@ -20,20 +20,26 @@ function linearIssue(identifier: string, comments: string[] = []) {
body,
url: `https://linear.app/example/issue/${identifier}#comment-${index}`,
}));
+ const nextPage = {
+ nodes: nodes.slice(1),
+ pageInfo: { hasNextPage: false },
+ async fetchNext() {
+ throw new Error("unexpected extra Linear comment page");
+ },
+ };
+ const firstPage = {
+ nodes: nodes.slice(0, 1),
+ pageInfo: { hasNextPage: nodes.length > 1 },
+ async fetchNext() {
+ return nextPage;
+ },
+ };
return {
identifier,
title: `Fix ${identifier}`,
description: `Synthetic evidence for ${identifier}`,
url: `https://linear.app/example/issue/${identifier}`,
- comments: async () => ({
- nodes: nodes.slice(0, 1),
- pageInfo: { hasNextPage: nodes.length > 1 },
- async fetchNext() {
- this.nodes.push(...nodes.slice(1));
- this.pageInfo.hasNextPage = false;
- return this;
- },
- }),
+ comments: async () => firstPage,
};
}
@@ -191,7 +197,7 @@ describe("CLI skill commands", () => {
description,
};
},
- } as ReturnType;
+ } as unknown as ReturnType;
},
onCodex: (_args, output, processEnvironment) => {
inputs = JSON.parse(output!.appServer!.prompt.split("\n").at(-1)!);
@@ -308,16 +314,19 @@ describe("CLI skill commands", () => {
environment: { LINEAR_ACCESS_TOKEN: "SYNTHETIC_OAUTH_TOKEN" },
linearClient: ({ accessToken }) => {
expect(accessToken).toBe("SYNTHETIC_OAUTH_TOKEN");
+ const nextPage = {
+ nodes: [linearIssue("SEC-124", ["Second issue comment"])],
+ pageInfo: { hasNextPage: false },
+ async fetchNext() {
+ throw new Error("unexpected extra Linear issue page");
+ },
+ };
const page = {
nodes: [linearIssue("SEC-123", ["First issue comment"])],
pageInfo: { hasNextPage: true },
async fetchNext() {
nextPages++;
- this.nodes.push(
- linearIssue("SEC-124", ["Second issue comment"]),
- );
- this.pageInfo.hasNextPage = false;
- return this;
+ return nextPage;
},
};
return {