chub-dev 0.1.0 → 0.1.2-beta.0

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.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1554 @@
1
+ ---
2
+ name: tracker
3
+ description: "Linear SDK for JavaScript/TypeScript for issue tracking and project management via GraphQL"
4
+ metadata:
5
+ languages: "javascript"
6
+ versions: "62.0.0"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "linear,tracker,issues,project-management,graphql"
10
+ ---
11
+
12
+ # Linear SDK for JavaScript/TypeScript
13
+
14
+ ## Golden Rule
15
+
16
+ **ALWAYS use `@linear/sdk` version 62.0.0 or later.**
17
+
18
+ Install with:
19
+ ```bash
20
+ npm install @linear/sdk
21
+ ```
22
+
23
+ **DO NOT use:**
24
+ - Unofficial Linear packages
25
+ - Direct GraphQL requests without the SDK (unless specifically required)
26
+ - Deprecated authentication methods
27
+ - Outdated Linear client libraries
28
+
29
+ The `@linear/sdk` is the official Linear TypeScript SDK that provides strongly-typed access to Linear's GraphQL API.
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ ### Install the SDK
36
+
37
+ ```bash
38
+ npm install @linear/sdk
39
+ ```
40
+
41
+ ### Environment Setup
42
+
43
+ Create a `.env` file:
44
+
45
+ ```bash
46
+ LINEAR_API_KEY=lin_api_your_personal_api_key_here
47
+ # OR for OAuth
48
+ LINEAR_ACCESS_TOKEN=your_oauth_access_token_here
49
+ ```
50
+
51
+ ### TypeScript Configuration (Optional)
52
+
53
+ The SDK ships with TypeScript types. No additional `@types` package needed.
54
+
55
+ ```json
56
+ {
57
+ "compilerOptions": {
58
+ "target": "ES2020",
59
+ "module": "commonjs",
60
+ "esModuleInterop": true,
61
+ "strict": true
62
+ }
63
+ }
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Initialization
69
+
70
+ ### Import the Client
71
+
72
+ ```typescript
73
+ import { LinearClient } from '@linear/sdk'
74
+ ```
75
+
76
+ ```javascript
77
+ const { LinearClient } = require('@linear/sdk')
78
+ ```
79
+
80
+ ### Authentication with API Key
81
+
82
+ ```typescript
83
+ import { LinearClient } from '@linear/sdk'
84
+
85
+ const client = new LinearClient({
86
+ apiKey: process.env.LINEAR_API_KEY
87
+ })
88
+ ```
89
+
90
+ ```javascript
91
+ const { LinearClient } = require('@linear/sdk')
92
+ require('dotenv').config()
93
+
94
+ const client = new LinearClient({
95
+ apiKey: process.env.LINEAR_API_KEY
96
+ })
97
+ ```
98
+
99
+ ### Authentication with OAuth 2.0
100
+
101
+ ```typescript
102
+ const client = new LinearClient({
103
+ accessToken: process.env.LINEAR_ACCESS_TOKEN
104
+ })
105
+ ```
106
+
107
+ ### Get Current User
108
+
109
+ ```typescript
110
+ async function getCurrentUser() {
111
+ const me = await client.viewer
112
+ console.log(`Logged in as: ${me.name} (${me.email})`)
113
+ return me
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Core API Surfaces
120
+
121
+ ### Issues
122
+
123
+ #### Fetch All Issues
124
+
125
+ **Minimal:**
126
+
127
+ ```typescript
128
+ async function getAllIssues() {
129
+ const issues = await client.issues()
130
+
131
+ issues.nodes.forEach(issue => {
132
+ console.log(`${issue.identifier}: ${issue.title}`)
133
+ })
134
+
135
+ return issues
136
+ }
137
+ ```
138
+
139
+ **Advanced with Pagination:**
140
+
141
+ ```typescript
142
+ async function getIssuesPaginated() {
143
+ const issues = await client.issues({
144
+ first: 50,
145
+ orderBy: 'createdAt'
146
+ })
147
+
148
+ for (const issue of issues.nodes) {
149
+ console.log(`[${issue.identifier}] ${issue.title}`)
150
+ console.log(` Status: ${(await issue.state)?.name}`)
151
+ console.log(` Assignee: ${(await issue.assignee)?.displayName || 'Unassigned'}`)
152
+ console.log(` Priority: ${issue.priority}`)
153
+ }
154
+
155
+ // Handle pagination
156
+ if (issues.pageInfo.hasNextPage) {
157
+ const nextPage = await client.issues({
158
+ first: 50,
159
+ after: issues.pageInfo.endCursor
160
+ })
161
+ // Process next page
162
+ }
163
+
164
+ return issues
165
+ }
166
+ ```
167
+
168
+ #### Get User's Assigned Issues
169
+
170
+ ```typescript
171
+ async function getMyIssues() {
172
+ const me = await client.viewer
173
+ const myIssues = await me.assignedIssues()
174
+
175
+ if (myIssues.nodes.length) {
176
+ myIssues.nodes.forEach(issue => {
177
+ console.log(`${me.displayName} has issue: ${issue.title}`)
178
+ })
179
+ }
180
+
181
+ return myIssues
182
+ }
183
+ ```
184
+
185
+ #### Query Single Issue by ID
186
+
187
+ ```typescript
188
+ async function getIssueById(issueId: string) {
189
+ const issue = await client.issue(issueId)
190
+
191
+ console.log(`Title: ${issue.title}`)
192
+ console.log(`Description: ${issue.description}`)
193
+ console.log(`Created: ${issue.createdAt}`)
194
+
195
+ const state = await issue.state
196
+ console.log(`State: ${state?.name}`)
197
+
198
+ const assignee = await issue.assignee
199
+ console.log(`Assignee: ${assignee?.displayName}`)
200
+
201
+ return issue
202
+ }
203
+
204
+ // Usage: Can use UUID or identifier like "ENG-123"
205
+ getIssueById("ENG-123")
206
+ ```
207
+
208
+ #### Create Issue
209
+
210
+ **Minimal:**
211
+
212
+ ```typescript
213
+ async function createIssue(teamId: string, title: string) {
214
+ const issuePayload = await client.issueCreate({
215
+ teamId: teamId,
216
+ title: title
217
+ })
218
+
219
+ if (issuePayload.success) {
220
+ console.log(`Created issue: ${issuePayload.issue?.identifier}`)
221
+ return issuePayload.issue
222
+ } else {
223
+ throw new Error('Failed to create issue')
224
+ }
225
+ }
226
+ ```
227
+
228
+ **Advanced with All Fields:**
229
+
230
+ ```typescript
231
+ async function createDetailedIssue() {
232
+ // First get team ID
233
+ const teams = await client.teams()
234
+ const team = teams.nodes[0]
235
+
236
+ // Get workflow state
237
+ const states = await client.workflowStates({
238
+ filter: { team: { id: { eq: team.id } } }
239
+ })
240
+ const todoState = states.nodes.find(s => s.name === 'Todo')
241
+
242
+ // Get labels
243
+ const labels = await client.issueLabels()
244
+ const bugLabel = labels.nodes.find(l => l.name === 'Bug')
245
+
246
+ const issuePayload = await client.issueCreate({
247
+ teamId: team.id,
248
+ title: 'Fix authentication error',
249
+ description: '## Problem\n\nUsers cannot log in\n\n## Steps to reproduce\n\n1. Go to login\n2. Enter credentials',
250
+ priority: 1, // 0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low
251
+ estimate: 3, // Story points
252
+ stateId: todoState?.id,
253
+ labelIds: bugLabel ? [bugLabel.id] : [],
254
+ assigneeId: (await client.viewer).id,
255
+ dueDate: new Date('2025-12-31')
256
+ })
257
+
258
+ if (issuePayload.success) {
259
+ return issuePayload.issue
260
+ }
261
+
262
+ throw new Error('Failed to create issue')
263
+ }
264
+ ```
265
+
266
+ #### Update Issue
267
+
268
+ **Minimal:**
269
+
270
+ ```typescript
271
+ async function updateIssue(issueId: string, title: string) {
272
+ const updatePayload = await client.issueUpdate(issueId, {
273
+ title: title
274
+ })
275
+
276
+ if (updatePayload.success) {
277
+ console.log('Issue updated')
278
+ return updatePayload.issue
279
+ }
280
+ }
281
+ ```
282
+
283
+ **Advanced:**
284
+
285
+ ```typescript
286
+ async function updateIssueComplete(issueId: string) {
287
+ // Get "Done" state
288
+ const issue = await client.issue(issueId)
289
+ const team = await issue.team
290
+ const states = await client.workflowStates({
291
+ filter: { team: { id: { eq: team.id } } }
292
+ })
293
+ const doneState = states.nodes.find(s => s.name === 'Done')
294
+
295
+ const updatePayload = await client.issueUpdate(issueId, {
296
+ stateId: doneState?.id,
297
+ title: 'Updated Issue Title',
298
+ description: 'New description',
299
+ priority: 3,
300
+ estimate: 5
301
+ })
302
+
303
+ if (updatePayload.success) {
304
+ console.log(`Updated: ${updatePayload.issue?.title}`)
305
+ return updatePayload.issue
306
+ }
307
+ }
308
+ ```
309
+
310
+ #### Filter Issues
311
+
312
+ ```typescript
313
+ async function filterIssues() {
314
+ const issues = await client.issues({
315
+ filter: {
316
+ assignee: {
317
+ email: { eq: 'user@example.com' }
318
+ },
319
+ state: {
320
+ name: { in: ['Todo', 'In Progress'] }
321
+ },
322
+ priority: {
323
+ gte: 2 // High priority and above
324
+ },
325
+ createdAt: {
326
+ gte: new Date('2025-01-01')
327
+ }
328
+ },
329
+ orderBy: 'priority',
330
+ first: 25
331
+ })
332
+
333
+ return issues.nodes
334
+ }
335
+ ```
336
+
337
+ #### Complex Filtering with AND/OR
338
+
339
+ ```typescript
340
+ async function complexFilterIssues() {
341
+ const issues = await client.issues({
342
+ filter: {
343
+ or: [
344
+ { priority: { eq: 1 } }, // Urgent
345
+ {
346
+ and: [
347
+ { priority: { eq: 2 } }, // High
348
+ { dueDate: { lte: new Date() } } // Overdue
349
+ ]
350
+ }
351
+ ]
352
+ }
353
+ })
354
+
355
+ return issues.nodes
356
+ }
357
+ ```
358
+
359
+ #### Archive Issue
360
+
361
+ ```typescript
362
+ async function archiveIssue(issueId: string) {
363
+ const payload = await client.issueArchive(issueId)
364
+
365
+ if (payload.success) {
366
+ console.log('Issue archived')
367
+ }
368
+
369
+ return payload
370
+ }
371
+ ```
372
+
373
+ ### Comments
374
+
375
+ #### Get Issue Comments
376
+
377
+ ```typescript
378
+ async function getIssueComments(issueId: string) {
379
+ const issue = await client.issue(issueId)
380
+ const comments = await issue.comments()
381
+
382
+ for (const comment of comments.nodes) {
383
+ const user = await comment.user
384
+ console.log(`${user?.displayName}: ${comment.body}`)
385
+ console.log(`Posted: ${comment.createdAt}`)
386
+ }
387
+
388
+ return comments
389
+ }
390
+ ```
391
+
392
+ #### Create Comment
393
+
394
+ **Minimal:**
395
+
396
+ ```typescript
397
+ async function addComment(issueId: string, body: string) {
398
+ const commentPayload = await client.commentCreate({
399
+ issueId: issueId,
400
+ body: body
401
+ })
402
+
403
+ if (commentPayload.success) {
404
+ console.log('Comment added')
405
+ return commentPayload.comment
406
+ }
407
+
408
+ throw new Error('Failed to create comment')
409
+ }
410
+ ```
411
+
412
+ **Advanced with Markdown:**
413
+
414
+ ```typescript
415
+ async function addDetailedComment(issueId: string) {
416
+ const body = `## Update
417
+
418
+ I've investigated this issue and found:
419
+
420
+ - The authentication token expires too quickly
421
+ - Need to implement refresh token logic
422
+
423
+ **Next steps:**
424
+ 1. Update token service
425
+ 2. Add refresh endpoint
426
+ 3. Test token renewal
427
+
428
+ cc @teammate`
429
+
430
+ const commentPayload = await client.commentCreate({
431
+ issueId: issueId,
432
+ body: body
433
+ })
434
+
435
+ return commentPayload.comment
436
+ }
437
+ ```
438
+
439
+ #### Update Comment
440
+
441
+ ```typescript
442
+ async function updateComment(commentId: string, newBody: string) {
443
+ const payload = await client.commentUpdate(commentId, {
444
+ body: newBody
445
+ })
446
+
447
+ if (payload.success) {
448
+ return payload.comment
449
+ }
450
+ }
451
+ ```
452
+
453
+ #### Delete Comment
454
+
455
+ ```typescript
456
+ async function deleteComment(commentId: string) {
457
+ const payload = await client.commentDelete(commentId)
458
+
459
+ if (payload.success) {
460
+ console.log('Comment deleted')
461
+ }
462
+
463
+ return payload
464
+ }
465
+ ```
466
+
467
+ ### Teams
468
+
469
+ #### Get All Teams
470
+
471
+ ```typescript
472
+ async function getTeams() {
473
+ const teams = await client.teams()
474
+
475
+ teams.nodes.forEach(team => {
476
+ console.log(`${team.name} (${team.key})`)
477
+ })
478
+
479
+ return teams
480
+ }
481
+ ```
482
+
483
+ #### Get Team by ID
484
+
485
+ ```typescript
486
+ async function getTeam(teamId: string) {
487
+ const team = await client.team(teamId)
488
+
489
+ console.log(`Team: ${team.name}`)
490
+ console.log(`Key: ${team.key}`)
491
+ console.log(`Description: ${team.description}`)
492
+
493
+ return team
494
+ }
495
+ ```
496
+
497
+ #### Get Team Issues
498
+
499
+ ```typescript
500
+ async function getTeamIssues(teamId: string) {
501
+ const team = await client.team(teamId)
502
+ const issues = await team.issues({
503
+ first: 50,
504
+ orderBy: 'updatedAt'
505
+ })
506
+
507
+ console.log(`${team.name} has ${issues.nodes.length} issues`)
508
+
509
+ return issues
510
+ }
511
+ ```
512
+
513
+ #### Get Team Members
514
+
515
+ ```typescript
516
+ async function getTeamMembers(teamId: string) {
517
+ const team = await client.team(teamId)
518
+ const members = await team.members()
519
+
520
+ for (const member of members.nodes) {
521
+ console.log(`${member.displayName} - ${member.email}`)
522
+ }
523
+
524
+ return members
525
+ }
526
+ ```
527
+
528
+ ### Projects
529
+
530
+ #### Get All Projects
531
+
532
+ ```typescript
533
+ async function getProjects() {
534
+ const projects = await client.projects()
535
+
536
+ for (const project of projects.nodes) {
537
+ console.log(`${project.name}`)
538
+ console.log(` State: ${project.state}`)
539
+ console.log(` Progress: ${project.progress}%`)
540
+ }
541
+
542
+ return projects
543
+ }
544
+ ```
545
+
546
+ #### Get Project by ID
547
+
548
+ ```typescript
549
+ async function getProject(projectId: string) {
550
+ const project = await client.project(projectId)
551
+
552
+ console.log(`Project: ${project.name}`)
553
+ console.log(`Description: ${project.description}`)
554
+ console.log(`Start: ${project.startDate}`)
555
+ console.log(`Target: ${project.targetDate}`)
556
+
557
+ const lead = await project.lead
558
+ console.log(`Lead: ${lead?.displayName}`)
559
+
560
+ return project
561
+ }
562
+ ```
563
+
564
+ #### Get Project Issues
565
+
566
+ ```typescript
567
+ async function getProjectIssues(projectId: string) {
568
+ const project = await client.project(projectId)
569
+ const issues = await project.issues()
570
+
571
+ console.log(`${project.name} issues:`)
572
+ issues.nodes.forEach(issue => {
573
+ console.log(` - ${issue.identifier}: ${issue.title}`)
574
+ })
575
+
576
+ return issues
577
+ }
578
+ ```
579
+
580
+ #### Create Project
581
+
582
+ ```typescript
583
+ async function createProject(teamId: string) {
584
+ const payload = await client.projectCreate({
585
+ teamIds: [teamId],
586
+ name: 'Q4 Authentication Improvements',
587
+ description: 'Improve authentication flow and security',
588
+ state: 'started',
589
+ targetDate: new Date('2025-12-31')
590
+ })
591
+
592
+ if (payload.success) {
593
+ return payload.project
594
+ }
595
+ }
596
+ ```
597
+
598
+ #### Update Project
599
+
600
+ ```typescript
601
+ async function updateProject(projectId: string) {
602
+ const payload = await client.projectUpdate(projectId, {
603
+ state: 'completed',
604
+ progress: 100
605
+ })
606
+
607
+ if (payload.success) {
608
+ console.log('Project completed')
609
+ return payload.project
610
+ }
611
+ }
612
+ ```
613
+
614
+ ### Labels
615
+
616
+ #### Get All Labels
617
+
618
+ ```typescript
619
+ async function getLabels() {
620
+ const labels = await client.issueLabels()
621
+
622
+ labels.nodes.forEach(label => {
623
+ console.log(`${label.name} - ${label.color}`)
624
+ })
625
+
626
+ return labels
627
+ }
628
+ ```
629
+
630
+ #### Get Label by ID
631
+
632
+ ```typescript
633
+ async function getLabel(labelId: string) {
634
+ const label = await client.issueLabel(labelId)
635
+
636
+ console.log(`Label: ${label.name}`)
637
+ console.log(`Description: ${label.description}`)
638
+
639
+ return label
640
+ }
641
+ ```
642
+
643
+ #### Create Label
644
+
645
+ ```typescript
646
+ async function createLabel(teamId: string) {
647
+ const payload = await client.issueLabelCreate({
648
+ teamId: teamId,
649
+ name: 'security',
650
+ description: 'Security related issues',
651
+ color: '#ff0000'
652
+ })
653
+
654
+ if (payload.success) {
655
+ return payload.issueLabel
656
+ }
657
+ }
658
+ ```
659
+
660
+ #### Filter Issues by Label
661
+
662
+ ```typescript
663
+ async function getIssuesByLabel(labelName: string) {
664
+ const issues = await client.issues({
665
+ filter: {
666
+ labels: {
667
+ name: { eq: labelName }
668
+ }
669
+ }
670
+ })
671
+
672
+ return issues.nodes
673
+ }
674
+ ```
675
+
676
+ ### Workflow States
677
+
678
+ #### Get All Workflow States
679
+
680
+ ```typescript
681
+ async function getWorkflowStates() {
682
+ const states = await client.workflowStates()
683
+
684
+ states.nodes.forEach(state => {
685
+ console.log(`${state.name} (${state.type})`)
686
+ })
687
+
688
+ return states
689
+ }
690
+ ```
691
+
692
+ #### Get Workflow States for Team
693
+
694
+ ```typescript
695
+ async function getTeamWorkflowStates(teamId: string) {
696
+ const states = await client.workflowStates({
697
+ filter: {
698
+ team: { id: { eq: teamId } }
699
+ }
700
+ })
701
+
702
+ console.log('Available states:')
703
+ states.nodes.forEach(state => {
704
+ console.log(` - ${state.name}`)
705
+ })
706
+
707
+ return states
708
+ }
709
+ ```
710
+
711
+ #### Get Issues in Specific State
712
+
713
+ ```typescript
714
+ async function getIssuesInState(stateId: string) {
715
+ const state = await client.workflowState(stateId)
716
+ const issues = await state.issues()
717
+
718
+ console.log(`Issues in "${state.name}":`)
719
+ issues.nodes.forEach(issue => {
720
+ console.log(` ${issue.identifier}: ${issue.title}`)
721
+ })
722
+
723
+ return issues
724
+ }
725
+ ```
726
+
727
+ ### Users
728
+
729
+ #### Get Current User
730
+
731
+ ```typescript
732
+ async function getCurrentUser() {
733
+ const me = await client.viewer
734
+
735
+ console.log(`Name: ${me.displayName}`)
736
+ console.log(`Email: ${me.email}`)
737
+ console.log(`Admin: ${me.admin}`)
738
+
739
+ return me
740
+ }
741
+ ```
742
+
743
+ #### Get User by ID
744
+
745
+ ```typescript
746
+ async function getUser(userId: string) {
747
+ const user = await client.user(userId)
748
+
749
+ console.log(`${user.displayName}`)
750
+ console.log(`Email: ${user.email}`)
751
+ console.log(`Active: ${user.active}`)
752
+
753
+ return user
754
+ }
755
+ ```
756
+
757
+ #### Get All Users
758
+
759
+ ```typescript
760
+ async function getUsers() {
761
+ const users = await client.users()
762
+
763
+ users.nodes.forEach(user => {
764
+ console.log(`${user.displayName} - ${user.email}`)
765
+ })
766
+
767
+ return users
768
+ }
769
+ ```
770
+
771
+ #### Get User's Teams
772
+
773
+ ```typescript
774
+ async function getUserTeams(userId: string) {
775
+ const user = await client.user(userId)
776
+ const teams = await user.teams()
777
+
778
+ console.log(`${user.displayName}'s teams:`)
779
+ teams.nodes.forEach(team => {
780
+ console.log(` - ${team.name}`)
781
+ })
782
+
783
+ return teams
784
+ }
785
+ ```
786
+
787
+ ### Cycles
788
+
789
+ #### Get Active Cycles
790
+
791
+ ```typescript
792
+ async function getActiveCycles() {
793
+ const cycles = await client.cycles({
794
+ filter: {
795
+ isActive: { eq: true }
796
+ }
797
+ })
798
+
799
+ for (const cycle of cycles.nodes) {
800
+ console.log(`${cycle.name}`)
801
+ console.log(` Start: ${cycle.startsAt}`)
802
+ console.log(` End: ${cycle.endsAt}`)
803
+ console.log(` Progress: ${cycle.progress}%`)
804
+ }
805
+
806
+ return cycles
807
+ }
808
+ ```
809
+
810
+ #### Get Cycle Issues
811
+
812
+ ```typescript
813
+ async function getCycleIssues(cycleId: string) {
814
+ const cycle = await client.cycle(cycleId)
815
+ const issues = await cycle.issues()
816
+
817
+ console.log(`${cycle.name} issues:`)
818
+ issues.nodes.forEach(issue => {
819
+ console.log(` ${issue.identifier}: ${issue.title}`)
820
+ })
821
+
822
+ return issues
823
+ }
824
+ ```
825
+
826
+ ### Attachments
827
+
828
+ #### Create Attachment
829
+
830
+ ```typescript
831
+ async function createAttachment(issueId: string) {
832
+ const payload = await client.attachmentCreate({
833
+ issueId: issueId,
834
+ title: 'Design Mockup',
835
+ url: 'https://example.com/mockup.png',
836
+ subtitle: 'Login page redesign'
837
+ })
838
+
839
+ if (payload.success) {
840
+ return payload.attachment
841
+ }
842
+ }
843
+ ```
844
+
845
+ #### Get Issue Attachments
846
+
847
+ ```typescript
848
+ async function getIssueAttachments(issueId: string) {
849
+ const issue = await client.issue(issueId)
850
+ const attachments = await issue.attachments()
851
+
852
+ attachments.nodes.forEach(attachment => {
853
+ console.log(`${attachment.title}: ${attachment.url}`)
854
+ })
855
+
856
+ return attachments
857
+ }
858
+ ```
859
+
860
+ ### Webhooks
861
+
862
+ #### Create Webhook
863
+
864
+ ```typescript
865
+ async function createWebhook() {
866
+ const payload = await client.webhookCreate({
867
+ url: 'https://example.com/webhook',
868
+ label: 'Issue Updates',
869
+ resourceTypes: ['Issue', 'Comment'],
870
+ enabled: true
871
+ })
872
+
873
+ if (payload.success) {
874
+ console.log(`Webhook created: ${payload.webhook?.id}`)
875
+ return payload.webhook
876
+ }
877
+ }
878
+ ```
879
+
880
+ #### Get Webhooks
881
+
882
+ ```typescript
883
+ async function getWebhooks() {
884
+ const webhooks = await client.webhooks()
885
+
886
+ webhooks.nodes.forEach(webhook => {
887
+ console.log(`${webhook.label}`)
888
+ console.log(` URL: ${webhook.url}`)
889
+ console.log(` Enabled: ${webhook.enabled}`)
890
+ console.log(` Resources: ${webhook.resourceTypes.join(', ')}`)
891
+ })
892
+
893
+ return webhooks
894
+ }
895
+ ```
896
+
897
+ #### Update Webhook
898
+
899
+ ```typescript
900
+ async function updateWebhook(webhookId: string) {
901
+ const payload = await client.webhookUpdate(webhookId, {
902
+ enabled: false
903
+ })
904
+
905
+ if (payload.success) {
906
+ return payload.webhook
907
+ }
908
+ }
909
+ ```
910
+
911
+ #### Delete Webhook
912
+
913
+ ```typescript
914
+ async function deleteWebhook(webhookId: string) {
915
+ const payload = await client.webhookDelete(webhookId)
916
+
917
+ if (payload.success) {
918
+ console.log('Webhook deleted')
919
+ }
920
+
921
+ return payload
922
+ }
923
+ ```
924
+
925
+ ### Search
926
+
927
+ #### Search Issues
928
+
929
+ ```typescript
930
+ async function searchIssues(query: string) {
931
+ const results = await client.searchIssues(query)
932
+
933
+ results.nodes.forEach(issue => {
934
+ console.log(`${issue.identifier}: ${issue.title}`)
935
+ })
936
+
937
+ return results
938
+ }
939
+
940
+ // Usage
941
+ searchIssues('authentication bug')
942
+ ```
943
+
944
+ #### Search Projects
945
+
946
+ ```typescript
947
+ async function searchProjects(query: string) {
948
+ const results = await client.searchProjects(query)
949
+
950
+ results.nodes.forEach(project => {
951
+ console.log(`${project.name}`)
952
+ })
953
+
954
+ return results
955
+ }
956
+ ```
957
+
958
+ ---
959
+
960
+ ## Pagination
961
+
962
+ ### Basic Pagination
963
+
964
+ ```typescript
965
+ async function paginateIssues() {
966
+ let allIssues = []
967
+ let hasNextPage = true
968
+ let cursor = null
969
+
970
+ while (hasNextPage) {
971
+ const issues = await client.issues({
972
+ first: 50,
973
+ after: cursor
974
+ })
975
+
976
+ allIssues.push(...issues.nodes)
977
+
978
+ hasNextPage = issues.pageInfo.hasNextPage
979
+ cursor = issues.pageInfo.endCursor
980
+ }
981
+
982
+ console.log(`Total issues: ${allIssues.length}`)
983
+ return allIssues
984
+ }
985
+ ```
986
+
987
+ ### Reverse Pagination
988
+
989
+ ```typescript
990
+ async function paginateReverse() {
991
+ const issues = await client.issues({
992
+ last: 25,
993
+ before: someCursor
994
+ })
995
+
996
+ return issues.nodes
997
+ }
998
+ ```
999
+
1000
+ ---
1001
+
1002
+ ## Error Handling
1003
+
1004
+ ### Try-Catch Pattern
1005
+
1006
+ ```typescript
1007
+ async function safeCreateIssue(teamId: string, title: string) {
1008
+ try {
1009
+ const payload = await client.issueCreate({
1010
+ teamId: teamId,
1011
+ title: title
1012
+ })
1013
+
1014
+ if (!payload.success) {
1015
+ console.error('Issue creation failed')
1016
+ return null
1017
+ }
1018
+
1019
+ return payload.issue
1020
+ } catch (error) {
1021
+ console.error('Error creating issue:', error.message)
1022
+ throw error
1023
+ }
1024
+ }
1025
+ ```
1026
+
1027
+ ### Check Success Field
1028
+
1029
+ ```typescript
1030
+ async function updateWithCheck(issueId: string) {
1031
+ const payload = await client.issueUpdate(issueId, {
1032
+ title: 'Updated Title'
1033
+ })
1034
+
1035
+ if (payload.success) {
1036
+ console.log('Update successful')
1037
+ return payload.issue
1038
+ } else {
1039
+ console.error('Update failed')
1040
+ return null
1041
+ }
1042
+ }
1043
+ ```
1044
+
1045
+ ---
1046
+
1047
+ ## Advanced Operations
1048
+
1049
+ ### Batch Operations
1050
+
1051
+ ```typescript
1052
+ async function batchCreateIssues(teamId: string, titles: string[]) {
1053
+ const promises = titles.map(title =>
1054
+ client.issueCreate({
1055
+ teamId: teamId,
1056
+ title: title
1057
+ })
1058
+ )
1059
+
1060
+ const results = await Promise.all(promises)
1061
+
1062
+ const created = results.filter(r => r.success)
1063
+ console.log(`Created ${created.length}/${titles.length} issues`)
1064
+
1065
+ return created.map(r => r.issue)
1066
+ }
1067
+ ```
1068
+
1069
+ ### Complex Relationships
1070
+
1071
+ ```typescript
1072
+ async function getCompleteIssueData(issueId: string) {
1073
+ const issue = await client.issue(issueId)
1074
+
1075
+ // Fetch all related data
1076
+ const [state, assignee, team, comments, attachments, labels, project, cycle] = await Promise.all([
1077
+ issue.state,
1078
+ issue.assignee,
1079
+ issue.team,
1080
+ issue.comments(),
1081
+ issue.attachments(),
1082
+ issue.labels(),
1083
+ issue.project,
1084
+ issue.cycle
1085
+ ])
1086
+
1087
+ return {
1088
+ issue,
1089
+ state,
1090
+ assignee,
1091
+ team,
1092
+ comments: comments.nodes,
1093
+ attachments: attachments.nodes,
1094
+ labels: labels.nodes,
1095
+ project,
1096
+ cycle
1097
+ }
1098
+ }
1099
+ ```
1100
+
1101
+ ### Conditional Updates
1102
+
1103
+ ```typescript
1104
+ async function updateIfAssigned(issueId: string) {
1105
+ const issue = await client.issue(issueId)
1106
+ const assignee = await issue.assignee
1107
+
1108
+ if (assignee) {
1109
+ await client.issueUpdate(issueId, {
1110
+ priority: 2 // Increase priority if assigned
1111
+ })
1112
+ }
1113
+ }
1114
+ ```
1115
+
1116
+ ### Archive Completed Issues
1117
+
1118
+ ```typescript
1119
+ async function archiveCompletedIssues(teamId: string) {
1120
+ const team = await client.team(teamId)
1121
+ const states = await client.workflowStates({
1122
+ filter: { team: { id: { eq: teamId } } }
1123
+ })
1124
+
1125
+ const doneState = states.nodes.find(s => s.name === 'Done')
1126
+
1127
+ if (!doneState) return
1128
+
1129
+ const issues = await client.issues({
1130
+ filter: {
1131
+ state: { id: { eq: doneState.id } },
1132
+ completedAt: { lte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) } // 30 days ago
1133
+ }
1134
+ })
1135
+
1136
+ for (const issue of issues.nodes) {
1137
+ await client.issueArchive(issue.id)
1138
+ console.log(`Archived: ${issue.identifier}`)
1139
+ }
1140
+ }
1141
+ ```
1142
+
1143
+ ---
1144
+
1145
+ ## Real-World Examples
1146
+
1147
+ ### Daily Standup Report
1148
+
1149
+ ```typescript
1150
+ async function generateStandupReport() {
1151
+ const me = await client.viewer
1152
+ const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000)
1153
+
1154
+ // Issues completed yesterday
1155
+ const completed = await client.issues({
1156
+ filter: {
1157
+ assignee: { id: { eq: me.id } },
1158
+ completedAt: { gte: yesterday }
1159
+ }
1160
+ })
1161
+
1162
+ // Issues in progress
1163
+ const inProgress = await client.issues({
1164
+ filter: {
1165
+ assignee: { id: { eq: me.id } },
1166
+ state: { name: { eq: 'In Progress' } }
1167
+ }
1168
+ })
1169
+
1170
+ console.log('## Completed Yesterday')
1171
+ completed.nodes.forEach(i => console.log(`- ${i.identifier}: ${i.title}`))
1172
+
1173
+ console.log('\n## In Progress')
1174
+ inProgress.nodes.forEach(i => console.log(`- ${i.identifier}: ${i.title}`))
1175
+
1176
+ return { completed: completed.nodes, inProgress: inProgress.nodes }
1177
+ }
1178
+ ```
1179
+
1180
+ ### Team Velocity Report
1181
+
1182
+ ```typescript
1183
+ async function calculateTeamVelocity(teamId: string, days: number = 7) {
1184
+ const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
1185
+
1186
+ const completed = await client.issues({
1187
+ filter: {
1188
+ team: { id: { eq: teamId } },
1189
+ completedAt: { gte: since }
1190
+ }
1191
+ })
1192
+
1193
+ let totalEstimate = 0
1194
+ for (const issue of completed.nodes) {
1195
+ totalEstimate += issue.estimate || 0
1196
+ }
1197
+
1198
+ console.log(`Team completed ${completed.nodes.length} issues`)
1199
+ console.log(`Total points: ${totalEstimate}`)
1200
+ console.log(`Velocity: ${(totalEstimate / days * 7).toFixed(1)} points/week`)
1201
+
1202
+ return {
1203
+ issueCount: completed.nodes.length,
1204
+ totalPoints: totalEstimate,
1205
+ velocityPerWeek: totalEstimate / days * 7
1206
+ }
1207
+ }
1208
+ ```
1209
+
1210
+ ### Bug Triage
1211
+
1212
+ ```typescript
1213
+ async function triageBugs(teamId: string) {
1214
+ const labels = await client.issueLabels()
1215
+ const bugLabel = labels.nodes.find(l => l.name.toLowerCase() === 'bug')
1216
+
1217
+ if (!bugLabel) {
1218
+ console.log('No bug label found')
1219
+ return
1220
+ }
1221
+
1222
+ const bugs = await client.issues({
1223
+ filter: {
1224
+ team: { id: { eq: teamId } },
1225
+ labels: { id: { eq: bugLabel.id } },
1226
+ priority: { eq: 0 } // No priority set
1227
+ },
1228
+ orderBy: 'createdAt'
1229
+ })
1230
+
1231
+ console.log(`Found ${bugs.nodes.length} untriaged bugs`)
1232
+
1233
+ for (const bug of bugs.nodes) {
1234
+ console.log(`\n${bug.identifier}: ${bug.title}`)
1235
+ console.log(`Created: ${bug.createdAt}`)
1236
+
1237
+ // Auto-prioritize based on keywords
1238
+ const desc = (bug.description || '').toLowerCase()
1239
+ let priority = 4 // Low
1240
+
1241
+ if (desc.includes('crash') || desc.includes('error')) {
1242
+ priority = 2 // High
1243
+ } else if (desc.includes('login') || desc.includes('payment')) {
1244
+ priority = 1 // Urgent
1245
+ }
1246
+
1247
+ await client.issueUpdate(bug.id, { priority })
1248
+ console.log(`Set priority to ${priority}`)
1249
+ }
1250
+ }
1251
+ ```
1252
+
1253
+ ### Sync GitHub Issues to Linear
1254
+
1255
+ ```typescript
1256
+ async function syncGitHubIssue(teamId: string, githubIssue: any) {
1257
+ const existingIssues = await client.issues({
1258
+ filter: {
1259
+ team: { id: { eq: teamId } },
1260
+ title: { contains: githubIssue.title }
1261
+ }
1262
+ })
1263
+
1264
+ if (existingIssues.nodes.length > 0) {
1265
+ console.log('Issue already exists')
1266
+ return existingIssues.nodes[0]
1267
+ }
1268
+
1269
+ const labels = await client.issueLabels()
1270
+ const githubLabel = labels.nodes.find(l => l.name === 'github')
1271
+
1272
+ const payload = await client.issueCreate({
1273
+ teamId: teamId,
1274
+ title: githubIssue.title,
1275
+ description: `${githubIssue.body}\n\n---\nSource: ${githubIssue.html_url}`,
1276
+ labelIds: githubLabel ? [githubLabel.id] : []
1277
+ })
1278
+
1279
+ if (payload.success) {
1280
+ console.log(`Created Linear issue: ${payload.issue?.identifier}`)
1281
+ return payload.issue
1282
+ }
1283
+ }
1284
+ ```
1285
+
1286
+ ### Automated Sprint Planning
1287
+
1288
+ ```typescript
1289
+ async function planSprint(teamId: string, sprintCapacity: number) {
1290
+ const states = await client.workflowStates({
1291
+ filter: { team: { id: { eq: teamId } } }
1292
+ })
1293
+ const backlogState = states.nodes.find(s => s.name === 'Backlog')
1294
+ const todoState = states.nodes.find(s => s.name === 'Todo')
1295
+
1296
+ if (!backlogState || !todoState) return
1297
+
1298
+ const backlogIssues = await client.issues({
1299
+ filter: {
1300
+ team: { id: { eq: teamId } },
1301
+ state: { id: { eq: backlogState.id } }
1302
+ },
1303
+ orderBy: 'priority'
1304
+ })
1305
+
1306
+ let totalEstimate = 0
1307
+ const sprintIssues = []
1308
+
1309
+ for (const issue of backlogIssues.nodes) {
1310
+ const estimate = issue.estimate || 1
1311
+
1312
+ if (totalEstimate + estimate <= sprintCapacity) {
1313
+ await client.issueUpdate(issue.id, {
1314
+ stateId: todoState.id
1315
+ })
1316
+
1317
+ sprintIssues.push(issue)
1318
+ totalEstimate += estimate
1319
+
1320
+ console.log(`Added to sprint: ${issue.identifier} (${estimate} points)`)
1321
+ }
1322
+
1323
+ if (totalEstimate >= sprintCapacity) break
1324
+ }
1325
+
1326
+ console.log(`\nSprint planned with ${totalEstimate}/${sprintCapacity} points`)
1327
+ return sprintIssues
1328
+ }
1329
+ ```
1330
+
1331
+ ---
1332
+
1333
+ ## Environment Variable Configuration
1334
+
1335
+ ### Complete Setup Example
1336
+
1337
+ ```typescript
1338
+ import { LinearClient } from '@linear/sdk'
1339
+ import * as dotenv from 'dotenv'
1340
+
1341
+ dotenv.config()
1342
+
1343
+ interface LinearConfig {
1344
+ apiKey?: string
1345
+ accessToken?: string
1346
+ }
1347
+
1348
+ function createLinearClient(): LinearClient {
1349
+ const config: LinearConfig = {}
1350
+
1351
+ if (process.env.LINEAR_API_KEY) {
1352
+ config.apiKey = process.env.LINEAR_API_KEY
1353
+ } else if (process.env.LINEAR_ACCESS_TOKEN) {
1354
+ config.accessToken = process.env.LINEAR_ACCESS_TOKEN
1355
+ } else {
1356
+ throw new Error('LINEAR_API_KEY or LINEAR_ACCESS_TOKEN must be set')
1357
+ }
1358
+
1359
+ return new LinearClient(config)
1360
+ }
1361
+
1362
+ export const client = createLinearClient()
1363
+ ```
1364
+
1365
+ ---
1366
+
1367
+ ## Type Definitions
1368
+
1369
+ ### Important Types
1370
+
1371
+ ```typescript
1372
+ import {
1373
+ LinearClient,
1374
+ LinearFetch,
1375
+ User,
1376
+ Issue,
1377
+ IssueConnection,
1378
+ Team,
1379
+ Project,
1380
+ Comment,
1381
+ WorkflowState,
1382
+ IssueLabel
1383
+ } from '@linear/sdk'
1384
+
1385
+ // LinearFetch is a Promise-like type
1386
+ const user: LinearFetch<User> = client.viewer
1387
+
1388
+ // Connections have nodes and pageInfo
1389
+ const issues: LinearFetch<IssueConnection> = client.issues()
1390
+
1391
+ // Access the actual data
1392
+ const issuesData = await issues
1393
+ const firstIssue: Issue = issuesData.nodes[0]
1394
+ ```
1395
+
1396
+ ### Custom Types
1397
+
1398
+ ```typescript
1399
+ interface IssueWithDetails {
1400
+ issue: Issue
1401
+ state: WorkflowState | null
1402
+ assignee: User | null
1403
+ team: Team
1404
+ labels: IssueLabel[]
1405
+ }
1406
+
1407
+ async function getIssueWithDetails(id: string): Promise<IssueWithDetails> {
1408
+ const issue = await client.issue(id)
1409
+
1410
+ return {
1411
+ issue,
1412
+ state: await issue.state,
1413
+ assignee: await issue.assignee,
1414
+ team: await issue.team,
1415
+ labels: (await issue.labels()).nodes
1416
+ }
1417
+ }
1418
+ ```
1419
+
1420
+ ---
1421
+
1422
+ ## Priority Values
1423
+
1424
+ ```typescript
1425
+ // Priority mapping
1426
+ const PRIORITY = {
1427
+ NONE: 0,
1428
+ URGENT: 1,
1429
+ HIGH: 2,
1430
+ MEDIUM: 3,
1431
+ LOW: 4
1432
+ }
1433
+
1434
+ // Usage
1435
+ await client.issueCreate({
1436
+ teamId: teamId,
1437
+ title: 'Critical bug',
1438
+ priority: PRIORITY.URGENT
1439
+ })
1440
+ ```
1441
+
1442
+ ---
1443
+
1444
+ ## GraphQL Direct Queries (Advanced)
1445
+
1446
+ ### Raw GraphQL Query
1447
+
1448
+ ```typescript
1449
+ async function rawGraphQL() {
1450
+ const query = `
1451
+ query {
1452
+ viewer {
1453
+ id
1454
+ name
1455
+ assignedIssues(first: 10) {
1456
+ nodes {
1457
+ id
1458
+ title
1459
+ state {
1460
+ name
1461
+ }
1462
+ }
1463
+ }
1464
+ }
1465
+ }
1466
+ `
1467
+
1468
+ // The SDK wraps the GraphQL API, but you can access raw client if needed
1469
+ // For most use cases, use the typed SDK methods instead
1470
+ }
1471
+ ```
1472
+
1473
+ ---
1474
+
1475
+ ## Complete Application Example
1476
+
1477
+ ```typescript
1478
+ import { LinearClient } from '@linear/sdk'
1479
+ import * as dotenv from 'dotenv'
1480
+
1481
+ dotenv.config()
1482
+
1483
+ const client = new LinearClient({
1484
+ apiKey: process.env.LINEAR_API_KEY
1485
+ })
1486
+
1487
+ async function main() {
1488
+ try {
1489
+ // Get current user
1490
+ const me = await client.viewer
1491
+ console.log(`Logged in as: ${me.displayName}`)
1492
+
1493
+ // Get teams
1494
+ const teams = await client.teams()
1495
+ const myTeam = teams.nodes[0]
1496
+ console.log(`Working with team: ${myTeam.name}`)
1497
+
1498
+ // Create an issue
1499
+ const issuePayload = await client.issueCreate({
1500
+ teamId: myTeam.id,
1501
+ title: 'Test issue from SDK',
1502
+ description: 'This is a test issue created via the Linear SDK',
1503
+ priority: 3
1504
+ })
1505
+
1506
+ if (issuePayload.success && issuePayload.issue) {
1507
+ const issue = issuePayload.issue
1508
+ console.log(`Created issue: ${issue.identifier}`)
1509
+
1510
+ // Add a comment
1511
+ const commentPayload = await client.commentCreate({
1512
+ issueId: issue.id,
1513
+ body: 'First comment on this issue!'
1514
+ })
1515
+
1516
+ if (commentPayload.success) {
1517
+ console.log('Comment added')
1518
+ }
1519
+
1520
+ // Get workflow states
1521
+ const states = await client.workflowStates({
1522
+ filter: { team: { id: { eq: myTeam.id } } }
1523
+ })
1524
+
1525
+ const inProgressState = states.nodes.find(s => s.name === 'In Progress')
1526
+
1527
+ if (inProgressState) {
1528
+ // Update issue state
1529
+ const updatePayload = await client.issueUpdate(issue.id, {
1530
+ stateId: inProgressState.id
1531
+ })
1532
+
1533
+ if (updatePayload.success) {
1534
+ console.log('Issue moved to In Progress')
1535
+ }
1536
+ }
1537
+
1538
+ // Get updated issue data
1539
+ const updatedIssue = await client.issue(issue.id)
1540
+ const currentState = await updatedIssue.state
1541
+ console.log(`Current state: ${currentState?.name}`)
1542
+
1543
+ // Get all comments
1544
+ const comments = await updatedIssue.comments()
1545
+ console.log(`Issue has ${comments.nodes.length} comment(s)`)
1546
+ }
1547
+
1548
+ } catch (error) {
1549
+ console.error('Error:', error.message)
1550
+ }
1551
+ }
1552
+
1553
+ main()
1554
+ ```