From 1055363f1df526e3ecad75f8eb8e71fd7080826f Mon Sep 17 00:00:00 2001 From: Cadu Date: Tue, 10 Feb 2026 19:49:50 -0300 Subject: [PATCH 1/2] fix(issues): handle null user on comments Automated/synced comments (e.g. GitHub integration) can have a null `user` field. Add a null check matching the existing pattern used for other optional fields (assignee, project, cycle, etc.) and mark the `user` field as optional in both the LinearIssue comments type and the LinearComment interface. Co-Authored-By: Claude Opus 4.6 --- src/utils/graphql-issues-service.ts | 10 +- src/utils/linear-types.d.ts | 4 +- ...l-issues-service-null-comment-user.test.ts | 92 +++++++++++++++++++ 3 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 tests/unit/graphql-issues-service-null-comment-user.test.ts diff --git a/src/utils/graphql-issues-service.ts b/src/utils/graphql-issues-service.ts index 6a5e1fe..61baba8 100644 --- a/src/utils/graphql-issues-service.ts +++ b/src/utils/graphql-issues-service.ts @@ -881,10 +881,12 @@ export class GraphQLIssuesService { id: comment.id, body: comment.body, embeds: extractEmbeds(comment.body), - user: { - id: comment.user.id, - name: comment.user.name, - }, + user: comment.user + ? { + id: comment.user.id, + name: comment.user.name, + } + : undefined, createdAt: comment.createdAt instanceof Date ? comment.createdAt.toISOString() : (comment.createdAt diff --git a/src/utils/linear-types.d.ts b/src/utils/linear-types.d.ts index ec24d51..29c775d 100644 --- a/src/utils/linear-types.d.ts +++ b/src/utils/linear-types.d.ts @@ -60,7 +60,7 @@ export interface LinearIssue { url: string; expiresAt: string; }>; - user: { + user?: { id: string; name: string; }; @@ -153,7 +153,7 @@ export interface CreateCommentArgs { export interface LinearComment { id: string; body: string; - user: { + user?: { id: string; name: string; }; diff --git a/tests/unit/graphql-issues-service-null-comment-user.test.ts b/tests/unit/graphql-issues-service-null-comment-user.test.ts new file mode 100644 index 0000000..d12dd49 --- /dev/null +++ b/tests/unit/graphql-issues-service-null-comment-user.test.ts @@ -0,0 +1,92 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { GraphQLIssuesService } from "../../src/utils/graphql-issues-service.js"; +import type { GraphQLService } from "../../src/utils/graphql-service.js"; + +/** + * Unit tests for null comment.user handling in doTransformIssueData. + * + * When a Linear issue has automated/synced comments (e.g. from GitHub + * integration), the `user` field on the comment can be `null`. + * Previously this caused: Cannot read properties of null (reading 'id'). + */ + +function makeIssueResponse(commentOverrides: Record = {}) { + return { + issue: { + id: "issue-1", + identifier: "ENG-100", + title: "Test Issue", + description: null, + branchName: null, + priority: 0, + estimate: null, + state: { id: "state-1", name: "In Progress" }, + assignee: null, + team: { id: "team-1", key: "ENG", name: "Engineering" }, + project: null, + cycle: null, + projectMilestone: null, + labels: { nodes: [] }, + parent: null, + children: { nodes: [] }, + comments: { + nodes: [ + { + id: "comment-1", + body: "Automated sync comment", + user: null, + createdAt: "2025-01-01T00:00:00Z", + updatedAt: "2025-01-01T00:00:00Z", + ...commentOverrides, + }, + ], + }, + createdAt: "2025-01-01T00:00:00Z", + updatedAt: "2025-01-01T00:00:00Z", + }, + }; +} + +describe("GraphQLIssuesService - Null Comment User", () => { + let mockGraphQLService: { rawRequest: ReturnType }; + let service: GraphQLIssuesService; + + beforeEach(() => { + mockGraphQLService = { + rawRequest: vi.fn(), + }; + service = new GraphQLIssuesService( + mockGraphQLService as unknown as GraphQLService, + ); + }); + + it("should handle comment with null user without crashing", async () => { + mockGraphQLService.rawRequest.mockResolvedValue(makeIssueResponse()); + + const result = await service.getIssueById( + "550e8400-e29b-41d4-a716-446655440000", + ); + + expect(result.comments).toHaveLength(1); + expect(result.comments![0].user).toBeUndefined(); + expect(result.comments![0].body).toBe("Automated sync comment"); + }); + + it("should preserve user when present on comment", async () => { + mockGraphQLService.rawRequest.mockResolvedValue( + makeIssueResponse({ + user: { id: "user-1", name: "Alice" }, + }), + ); + + const result = await service.getIssueById( + "550e8400-e29b-41d4-a716-446655440000", + ); + + expect(result.comments).toHaveLength(1); + expect(result.comments![0].user).toEqual({ + id: "user-1", + name: "Alice", + }); + }); +}); From a9c1c38cc6f1b47b384704b4f0b8ab8ddad3877b Mon Sep 17 00:00:00 2001 From: Cadu Date: Tue, 10 Feb 2026 20:01:21 -0300 Subject: [PATCH 2/2] build: add prepare script for git-based installs npm doesn't install devDependencies in its git clone temp directory before running lifecycle scripts, so `tsc` is unavailable. Use `npx --yes --package typescript tsc` to self-bootstrap the build. This enables `npm install -g github:user/linearis#ref`. Co-Authored-By: Claude Opus 4.6 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 33b8887..07293fa 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "test:ui": "vitest --ui", "test:coverage": "vitest run --coverage", "test:commands": "tsx tests/command-coverage.ts", + "prepare": "npm_config_global=false npm install --ignore-scripts && tsc && chmod +x dist/main.js", "prepublishOnly": "npm run build && npm run test && test -x dist/main.js" }, "engines": {