@stackmemoryai/stackmemory 0.5.22 → 0.5.24

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 (77) hide show
  1. package/dist/cli/claude-sm.js +2 -0
  2. package/dist/cli/claude-sm.js.map +2 -2
  3. package/dist/cli/commands/discovery.js +279 -0
  4. package/dist/cli/commands/discovery.js.map +7 -0
  5. package/dist/cli/commands/retrieval.js +248 -0
  6. package/dist/cli/commands/retrieval.js.map +7 -0
  7. package/dist/cli/index.js +4 -0
  8. package/dist/cli/index.js.map +2 -2
  9. package/dist/core/retrieval/llm-context-retrieval.js +33 -1
  10. package/dist/core/retrieval/llm-context-retrieval.js.map +2 -2
  11. package/dist/core/retrieval/llm-provider.js +128 -0
  12. package/dist/core/retrieval/llm-provider.js.map +7 -0
  13. package/dist/core/retrieval/retrieval-audit.js +236 -0
  14. package/dist/core/retrieval/retrieval-audit.js.map +7 -0
  15. package/dist/integrations/linear/client.js +125 -0
  16. package/dist/integrations/linear/client.js.map +2 -2
  17. package/dist/integrations/mcp/handlers/discovery-handlers.js +497 -0
  18. package/dist/integrations/mcp/handlers/discovery-handlers.js.map +7 -0
  19. package/dist/integrations/mcp/handlers/index.js +40 -12
  20. package/dist/integrations/mcp/handlers/index.js.map +2 -2
  21. package/dist/integrations/mcp/server.js +270 -0
  22. package/dist/integrations/mcp/server.js.map +2 -2
  23. package/dist/integrations/mcp/tool-definitions.js +141 -5
  24. package/dist/integrations/mcp/tool-definitions.js.map +2 -2
  25. package/package.json +1 -1
  26. package/dist/cli/commands/agent.js +0 -286
  27. package/dist/cli/commands/agent.js.map +0 -7
  28. package/dist/cli/commands/chromadb.js +0 -482
  29. package/dist/cli/commands/chromadb.js.map +0 -7
  30. package/dist/cli/commands/gc.js +0 -251
  31. package/dist/cli/commands/gc.js.map +0 -7
  32. package/dist/cli/commands/infinite-storage.js +0 -292
  33. package/dist/cli/commands/infinite-storage.js.map +0 -7
  34. package/dist/cli/commands/linear-create.js +0 -171
  35. package/dist/cli/commands/linear-create.js.map +0 -7
  36. package/dist/cli/commands/linear-list.js +0 -103
  37. package/dist/cli/commands/linear-list.js.map +0 -7
  38. package/dist/cli/commands/linear-migrate.js +0 -64
  39. package/dist/cli/commands/linear-migrate.js.map +0 -7
  40. package/dist/cli/commands/linear-test.js +0 -134
  41. package/dist/cli/commands/linear-test.js.map +0 -7
  42. package/dist/cli/commands/tui.js +0 -77
  43. package/dist/cli/commands/tui.js.map +0 -7
  44. package/dist/cli/commands/webhook.js +0 -181
  45. package/dist/cli/commands/webhook.js.map +0 -7
  46. package/dist/cli/streamlined-cli.js +0 -144
  47. package/dist/cli/streamlined-cli.js.map +0 -7
  48. package/dist/core/events/event-bus.js +0 -110
  49. package/dist/core/events/event-bus.js.map +0 -7
  50. package/dist/core/frame/workflow-templates-stub.js +0 -42
  51. package/dist/core/frame/workflow-templates-stub.js.map +0 -7
  52. package/dist/core/plugins/plugin-interface.js +0 -87
  53. package/dist/core/plugins/plugin-interface.js.map +0 -7
  54. package/dist/core/session/clear-survival-stub.js +0 -53
  55. package/dist/core/session/clear-survival-stub.js.map +0 -7
  56. package/dist/core/storage/chromadb-simple.js +0 -172
  57. package/dist/core/storage/chromadb-simple.js.map +0 -7
  58. package/dist/core/storage/simplified-storage.js +0 -328
  59. package/dist/core/storage/simplified-storage.js.map +0 -7
  60. package/dist/features/tasks/pebbles-task-store.js +0 -647
  61. package/dist/features/tasks/pebbles-task-store.js.map +0 -7
  62. package/dist/integrations/linear/sync-enhanced.js +0 -202
  63. package/dist/integrations/linear/sync-enhanced.js.map +0 -7
  64. package/dist/plugins/linear/index.js +0 -166
  65. package/dist/plugins/linear/index.js.map +0 -7
  66. package/dist/plugins/loader.js +0 -57
  67. package/dist/plugins/loader.js.map +0 -7
  68. package/dist/plugins/plugin-interface.js +0 -67
  69. package/dist/plugins/plugin-interface.js.map +0 -7
  70. package/dist/plugins/ralph/simple-ralph-plugin.js +0 -305
  71. package/dist/plugins/ralph/simple-ralph-plugin.js.map +0 -7
  72. package/dist/plugins/ralph/use-cases/code-generator.js +0 -151
  73. package/dist/plugins/ralph/use-cases/code-generator.js.map +0 -7
  74. package/dist/plugins/ralph/use-cases/test-generator.js +0 -201
  75. package/dist/plugins/ralph/use-cases/test-generator.js.map +0 -7
  76. package/dist/utils/logger.js +0 -52
  77. package/dist/utils/logger.js.map +0 -7
@@ -463,6 +463,131 @@ class LinearClient {
463
463
  });
464
464
  return result.issues.nodes;
465
465
  }
466
+ /**
467
+ * Assign an issue to a user
468
+ */
469
+ async assignIssue(issueId, assigneeId) {
470
+ const mutation = `
471
+ mutation AssignIssue($issueId: String!, $assigneeId: String!) {
472
+ issueUpdate(id: $issueId, input: { assigneeId: $assigneeId }) {
473
+ success
474
+ issue {
475
+ id
476
+ identifier
477
+ title
478
+ assignee {
479
+ id
480
+ name
481
+ }
482
+ }
483
+ }
484
+ }
485
+ `;
486
+ const result = await this.graphql(mutation, { issueId, assigneeId });
487
+ return result.issueUpdate;
488
+ }
489
+ /**
490
+ * Update issue state (e.g., move to "In Progress")
491
+ */
492
+ async updateIssueState(issueId, stateId) {
493
+ const mutation = `
494
+ mutation UpdateIssueState($issueId: String!, $stateId: String!) {
495
+ issueUpdate(id: $issueId, input: { stateId: $stateId }) {
496
+ success
497
+ issue {
498
+ id
499
+ identifier
500
+ title
501
+ state {
502
+ id
503
+ name
504
+ type
505
+ }
506
+ }
507
+ }
508
+ }
509
+ `;
510
+ const result = await this.graphql(mutation, { issueId, stateId });
511
+ return result.issueUpdate;
512
+ }
513
+ /**
514
+ * Get an issue by ID with team info
515
+ */
516
+ async getIssueById(issueId) {
517
+ const query = `
518
+ query GetIssue($issueId: String!) {
519
+ issue(id: $issueId) {
520
+ id
521
+ identifier
522
+ title
523
+ description
524
+ state {
525
+ id
526
+ name
527
+ type
528
+ }
529
+ priority
530
+ assignee {
531
+ id
532
+ name
533
+ email
534
+ }
535
+ estimate
536
+ labels {
537
+ nodes {
538
+ id
539
+ name
540
+ }
541
+ }
542
+ team {
543
+ id
544
+ name
545
+ }
546
+ createdAt
547
+ updatedAt
548
+ url
549
+ }
550
+ }
551
+ `;
552
+ try {
553
+ const result = await this.graphql(query, { issueId });
554
+ return result.issue;
555
+ } catch {
556
+ return null;
557
+ }
558
+ }
559
+ /**
560
+ * Start working on an issue (assign to self and move to In Progress)
561
+ */
562
+ async startIssue(issueId) {
563
+ try {
564
+ const user = await this.getViewer();
565
+ const issue = await this.getIssueById(issueId);
566
+ if (!issue) {
567
+ return { success: false, error: "Issue not found" };
568
+ }
569
+ const assignResult = await this.assignIssue(issueId, user.id);
570
+ if (!assignResult.success) {
571
+ return { success: false, error: "Failed to assign issue" };
572
+ }
573
+ const teamId = issue.team?.id;
574
+ if (teamId) {
575
+ const states = await this.getWorkflowStates(teamId);
576
+ const inProgressState = states.find(
577
+ (s) => s.type === "started" || s.name.toLowerCase().includes("progress")
578
+ );
579
+ if (inProgressState) {
580
+ await this.updateIssueState(issueId, inProgressState.id);
581
+ }
582
+ }
583
+ return { success: true, issue: assignResult.issue };
584
+ } catch (error) {
585
+ return {
586
+ success: false,
587
+ error: error instanceof Error ? error.message : "Unknown error"
588
+ };
589
+ }
590
+ }
466
591
  }
467
592
  export {
468
593
  LinearClient
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/integrations/linear/client.ts"],
4
- "sourcesContent": ["/**\n * Linear API Client for StackMemory\n * Handles bi-directional sync with Linear's GraphQL API\n */\n\nimport { logger } from '../../core/monitoring/logger.js';\n\nexport interface LinearConfig {\n apiKey: string;\n teamId?: string;\n webhookSecret?: string;\n baseUrl?: string;\n // If true, send Authorization header as `Bearer <apiKey>` (OAuth access token)\n useBearer?: boolean;\n // Optional callback to refresh token on 401 and return the new access token\n onUnauthorized?: () => Promise<string>;\n}\n\nexport interface LinearIssue {\n id: string;\n identifier: string; // Like \"SM-123\"\n title: string;\n description?: string;\n state: {\n id: string;\n name: string;\n type: 'backlog' | 'unstarted' | 'started' | 'completed' | 'cancelled';\n };\n priority: number; // 0-4 (0=none, 1=urgent, 2=high, 3=medium, 4=low)\n assignee?: {\n id: string;\n name: string;\n email: string;\n };\n estimate?: number; // Story points\n labels: Array<{\n id: string;\n name: string;\n }>;\n createdAt: string;\n updatedAt: string;\n url: string;\n}\n\nexport interface LinearCreateIssueInput {\n title: string;\n description?: string;\n teamId: string;\n priority?: number;\n estimate?: number;\n labelIds?: string[];\n}\n\ninterface RateLimitState {\n remaining: number;\n resetAt: number;\n retryAfter: number;\n}\n\nexport class LinearClient {\n private config: LinearConfig;\n private baseUrl: string;\n private rateLimitState: RateLimitState = {\n remaining: 1500, // Linear's default limit\n resetAt: Date.now() + 3600000,\n retryAfter: 0,\n };\n private requestQueue: Array<() => Promise<void>> = [];\n private isProcessingQueue = false;\n private minRequestInterval = 100; // Minimum ms between requests\n private lastRequestTime = 0;\n\n constructor(config: LinearConfig) {\n this.config = config;\n this.baseUrl = config.baseUrl || 'https://api.linear.app';\n\n if (!config.apiKey) {\n throw new Error('Linear API key is required');\n }\n }\n\n /**\n * Wait for rate limit to reset if needed\n */\n private async waitForRateLimit(): Promise<void> {\n const now = Date.now();\n\n // Check if we're in a retry-after period\n if (this.rateLimitState.retryAfter > now) {\n const waitTime = this.rateLimitState.retryAfter - now;\n logger.warn(`Rate limited, waiting ${Math.ceil(waitTime / 1000)}s`);\n await this.sleep(waitTime);\n }\n\n // Check if we've exhausted our rate limit\n if (this.rateLimitState.remaining <= 5) {\n if (this.rateLimitState.resetAt > now) {\n const waitTime = this.rateLimitState.resetAt - now;\n logger.warn(\n `Rate limit nearly exhausted, waiting ${Math.ceil(waitTime / 1000)}s for reset`\n );\n await this.sleep(Math.min(waitTime, 60000)); // Max 60s wait\n }\n }\n\n // Ensure minimum interval between requests\n const timeSinceLastRequest = now - this.lastRequestTime;\n if (timeSinceLastRequest < this.minRequestInterval) {\n await this.sleep(this.minRequestInterval - timeSinceLastRequest);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Update rate limit state from response headers\n */\n private updateRateLimitState(response: Response): void {\n const remaining = response.headers.get('x-ratelimit-remaining');\n const reset = response.headers.get('x-ratelimit-reset');\n const retryAfter = response.headers.get('retry-after');\n\n if (remaining !== null) {\n this.rateLimitState.remaining = parseInt(remaining, 10);\n }\n if (reset !== null) {\n this.rateLimitState.resetAt = parseInt(reset, 10) * 1000;\n }\n if (retryAfter !== null) {\n this.rateLimitState.retryAfter =\n Date.now() + parseInt(retryAfter, 10) * 1000;\n }\n }\n\n /**\n * Execute GraphQL query against Linear API with rate limiting\n */\n private async graphql<T>(\n query: string,\n variables?: Record<string, unknown>,\n retries = 3,\n allowAuthRefresh = true\n ): Promise<T> {\n // Wait for rate limit before making request\n await this.waitForRateLimit();\n\n this.lastRequestTime = Date.now();\n\n const authHeader = this.config.useBearer\n ? `Bearer ${this.config.apiKey}`\n : this.config.apiKey;\n\n let response = await fetch(`${this.baseUrl}/graphql`, {\n method: 'POST',\n headers: {\n Authorization: authHeader,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n query,\n variables,\n }),\n });\n\n // Update rate limit state from response\n this.updateRateLimitState(response);\n\n // Handle unauthorized (e.g., expired OAuth token)\n if (\n response.status === 401 &&\n this.config.onUnauthorized &&\n allowAuthRefresh\n ) {\n try {\n const newToken = await this.config.onUnauthorized();\n // Update local config and retry once without further auth refresh\n this.config.apiKey = newToken;\n const retryHeader = this.config.useBearer\n ? `Bearer ${newToken}`\n : newToken;\n response = await fetch(`${this.baseUrl}/graphql`, {\n method: 'POST',\n headers: {\n Authorization: retryHeader,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ query, variables }),\n });\n this.updateRateLimitState(response);\n } catch (e: unknown) {\n // Fall through to standard error handling\n }\n }\n\n // Handle rate limiting with exponential backoff\n if (response.status === 429) {\n if (retries > 0) {\n const retryAfter = response.headers.get('retry-after');\n const waitTime = retryAfter ? parseInt(retryAfter, 10) * 1000 : 60000;\n logger.warn(\n `Rate limited (429), retrying in ${waitTime / 1000}s (${retries} retries left)`\n );\n this.rateLimitState.retryAfter = Date.now() + waitTime;\n await this.sleep(waitTime);\n return this.graphql<T>(query, variables, retries - 1, allowAuthRefresh);\n }\n throw new Error('Linear API rate limit exceeded after retries');\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n logger.error(\n 'Linear API error response:',\n new Error(`${response.status}: ${errorText}`)\n );\n throw new Error(\n `Linear API error: ${response.status} ${response.statusText} - ${errorText}`\n );\n }\n\n const result = (await response.json()) as {\n data?: T;\n errors?: Array<{ message: string }>;\n };\n\n if (result.errors) {\n // Check for rate limit errors in GraphQL response\n const rateLimitError = result.errors.find(\n (e) =>\n e.message.toLowerCase().includes('rate limit') ||\n e.message.toLowerCase().includes('usage limit')\n );\n\n if (rateLimitError && retries > 0) {\n const waitTime = 60000; // Default 60s wait for GraphQL rate limit errors\n logger.warn(\n `GraphQL rate limit error, retrying in ${waitTime / 1000}s (${retries} retries left)`\n );\n this.rateLimitState.retryAfter = Date.now() + waitTime;\n await this.sleep(waitTime);\n return this.graphql<T>(query, variables, retries - 1);\n }\n\n logger.error('Linear GraphQL errors:', { errors: result.errors });\n throw new Error(`Linear GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n }\n\n /**\n * Create a new issue in Linear\n */\n async createIssue(input: LinearCreateIssueInput): Promise<LinearIssue> {\n const mutation = `\n mutation CreateIssue($input: IssueCreateInput!) {\n issueCreate(input: $input) {\n success\n issue {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n issueCreate: {\n success: boolean;\n issue: LinearIssue;\n };\n }>(mutation, { input });\n\n if (!result.issueCreate.success) {\n throw new Error('Failed to create Linear issue');\n }\n\n return result.issueCreate.issue;\n }\n\n /**\n * Update an existing Linear issue\n */\n async updateIssue(\n issueId: string,\n updates: Partial<LinearCreateIssueInput> & { stateId?: string }\n ): Promise<LinearIssue> {\n const mutation = `\n mutation UpdateIssue($id: String!, $input: IssueUpdateInput!) {\n issueUpdate(id: $id, input: $input) {\n success\n issue {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n issueUpdate: {\n success: boolean;\n issue: LinearIssue;\n };\n }>(mutation, { id: issueId, input: updates });\n\n if (!result.issueUpdate.success) {\n throw new Error(`Failed to update Linear issue ${issueId}`);\n }\n\n return result.issueUpdate.issue;\n }\n\n /**\n * Get issue by ID\n */\n async getIssue(issueId: string): Promise<LinearIssue | null> {\n const query = `\n query GetIssue($id: String!) {\n issue(id: $id) {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n `;\n\n const result = await this.graphql<{\n issue: LinearIssue | null;\n }>(query, { id: issueId });\n\n return result.issue;\n }\n\n /**\n * Search for issues by identifier (e.g., \"SM-123\")\n */\n async findIssueByIdentifier(identifier: string): Promise<LinearIssue | null> {\n const query = `\n query FindIssue($filter: IssueFilter!) {\n issues(filter: $filter, first: 1) {\n nodes {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n issues: {\n nodes: LinearIssue[];\n };\n }>(query, {\n filter: {\n number: {\n eq: parseInt(identifier.split('-')[1] || '0') || 0,\n },\n },\n });\n\n return result.issues.nodes[0] || null;\n }\n\n /**\n * Get team information\n */\n async getTeam(\n teamId?: string\n ): Promise<{ id: string; name: string; key: string }> {\n const query = teamId\n ? `\n query GetTeam($id: String!) {\n team(id: $id) {\n id\n name\n key\n }\n }\n `\n : `\n query GetTeams {\n teams(first: 1) {\n nodes {\n id\n name\n key\n }\n }\n }\n `;\n\n if (teamId) {\n const result = await this.graphql<{\n team: { id: string; name: string; key: string };\n }>(query, { id: teamId });\n if (!result.team) {\n throw new Error(`Team ${teamId} not found`);\n }\n return result.team;\n } else {\n const result = await this.graphql<{\n teams: {\n nodes: Array<{ id: string; name: string; key: string }>;\n };\n }>(query);\n\n if (result.teams.nodes.length === 0) {\n throw new Error('No teams found');\n }\n\n return result.teams.nodes[0]!;\n }\n }\n\n /**\n * Get workflow states for a team\n */\n async getWorkflowStates(teamId: string): Promise<\n Array<{\n id: string;\n name: string;\n type: 'backlog' | 'unstarted' | 'started' | 'completed' | 'cancelled';\n color: string;\n }>\n > {\n const query = `\n query GetWorkflowStates($teamId: String!) {\n team(id: $teamId) {\n states {\n nodes {\n id\n name\n type\n color\n }\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n team: {\n states: {\n nodes: Array<{\n id: string;\n name: string;\n type:\n | 'backlog'\n | 'unstarted'\n | 'started'\n | 'completed'\n | 'cancelled';\n color: string;\n }>;\n };\n };\n }>(query, { teamId });\n\n return result.team.states.nodes;\n }\n\n /**\n * Get current viewer/user information\n */\n async getViewer(): Promise<{\n id: string;\n name: string;\n email: string;\n }> {\n const query = `\n query GetViewer {\n viewer {\n id\n name\n email\n }\n }\n `;\n\n const result = await this.graphql<{\n viewer: {\n id: string;\n name: string;\n email: string;\n };\n }>(query);\n\n return result.viewer;\n }\n\n /**\n * Get all teams for the organization\n */\n async getTeams(): Promise<\n Array<{\n id: string;\n name: string;\n key: string;\n }>\n > {\n const query = `\n query GetTeams {\n teams(first: 50) {\n nodes {\n id\n name\n key\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n teams: {\n nodes: Array<{\n id: string;\n name: string;\n key: string;\n }>;\n };\n }>(query);\n\n return result.teams.nodes;\n }\n\n /**\n * Get issues with filtering options\n */\n async getIssues(options?: {\n teamId?: string;\n assigneeId?: string;\n stateType?: 'backlog' | 'unstarted' | 'started' | 'completed' | 'cancelled';\n limit?: number;\n }): Promise<LinearIssue[]> {\n const query = `\n query GetIssues($filter: IssueFilter, $first: Int!) {\n issues(filter: $filter, first: $first) {\n nodes {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n }\n `;\n\n const filter: Record<string, unknown> = {};\n\n if (options?.teamId) {\n filter.team = { id: { eq: options.teamId } };\n }\n\n if (options?.assigneeId) {\n filter.assignee = { id: { eq: options.assigneeId } };\n }\n\n if (options?.stateType) {\n filter.state = { type: { eq: options.stateType } };\n }\n\n const result = await this.graphql<{\n issues: {\n nodes: LinearIssue[];\n };\n }>(query, {\n filter: Object.keys(filter).length > 0 ? filter : undefined,\n first: options?.limit || 50,\n });\n\n return result.issues.nodes;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Linear API Client for StackMemory\n * Handles bi-directional sync with Linear's GraphQL API\n */\n\nimport { logger } from '../../core/monitoring/logger.js';\n\nexport interface LinearConfig {\n apiKey: string;\n teamId?: string;\n webhookSecret?: string;\n baseUrl?: string;\n // If true, send Authorization header as `Bearer <apiKey>` (OAuth access token)\n useBearer?: boolean;\n // Optional callback to refresh token on 401 and return the new access token\n onUnauthorized?: () => Promise<string>;\n}\n\nexport interface LinearIssue {\n id: string;\n identifier: string; // Like \"SM-123\"\n title: string;\n description?: string;\n state: {\n id: string;\n name: string;\n type: 'backlog' | 'unstarted' | 'started' | 'completed' | 'cancelled';\n };\n priority: number; // 0-4 (0=none, 1=urgent, 2=high, 3=medium, 4=low)\n assignee?: {\n id: string;\n name: string;\n email: string;\n };\n estimate?: number; // Story points\n labels: Array<{\n id: string;\n name: string;\n }>;\n createdAt: string;\n updatedAt: string;\n url: string;\n}\n\nexport interface LinearCreateIssueInput {\n title: string;\n description?: string;\n teamId: string;\n priority?: number;\n estimate?: number;\n labelIds?: string[];\n}\n\ninterface RateLimitState {\n remaining: number;\n resetAt: number;\n retryAfter: number;\n}\n\nexport class LinearClient {\n private config: LinearConfig;\n private baseUrl: string;\n private rateLimitState: RateLimitState = {\n remaining: 1500, // Linear's default limit\n resetAt: Date.now() + 3600000,\n retryAfter: 0,\n };\n private requestQueue: Array<() => Promise<void>> = [];\n private isProcessingQueue = false;\n private minRequestInterval = 100; // Minimum ms between requests\n private lastRequestTime = 0;\n\n constructor(config: LinearConfig) {\n this.config = config;\n this.baseUrl = config.baseUrl || 'https://api.linear.app';\n\n if (!config.apiKey) {\n throw new Error('Linear API key is required');\n }\n }\n\n /**\n * Wait for rate limit to reset if needed\n */\n private async waitForRateLimit(): Promise<void> {\n const now = Date.now();\n\n // Check if we're in a retry-after period\n if (this.rateLimitState.retryAfter > now) {\n const waitTime = this.rateLimitState.retryAfter - now;\n logger.warn(`Rate limited, waiting ${Math.ceil(waitTime / 1000)}s`);\n await this.sleep(waitTime);\n }\n\n // Check if we've exhausted our rate limit\n if (this.rateLimitState.remaining <= 5) {\n if (this.rateLimitState.resetAt > now) {\n const waitTime = this.rateLimitState.resetAt - now;\n logger.warn(\n `Rate limit nearly exhausted, waiting ${Math.ceil(waitTime / 1000)}s for reset`\n );\n await this.sleep(Math.min(waitTime, 60000)); // Max 60s wait\n }\n }\n\n // Ensure minimum interval between requests\n const timeSinceLastRequest = now - this.lastRequestTime;\n if (timeSinceLastRequest < this.minRequestInterval) {\n await this.sleep(this.minRequestInterval - timeSinceLastRequest);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Update rate limit state from response headers\n */\n private updateRateLimitState(response: Response): void {\n const remaining = response.headers.get('x-ratelimit-remaining');\n const reset = response.headers.get('x-ratelimit-reset');\n const retryAfter = response.headers.get('retry-after');\n\n if (remaining !== null) {\n this.rateLimitState.remaining = parseInt(remaining, 10);\n }\n if (reset !== null) {\n this.rateLimitState.resetAt = parseInt(reset, 10) * 1000;\n }\n if (retryAfter !== null) {\n this.rateLimitState.retryAfter =\n Date.now() + parseInt(retryAfter, 10) * 1000;\n }\n }\n\n /**\n * Execute GraphQL query against Linear API with rate limiting\n */\n private async graphql<T>(\n query: string,\n variables?: Record<string, unknown>,\n retries = 3,\n allowAuthRefresh = true\n ): Promise<T> {\n // Wait for rate limit before making request\n await this.waitForRateLimit();\n\n this.lastRequestTime = Date.now();\n\n const authHeader = this.config.useBearer\n ? `Bearer ${this.config.apiKey}`\n : this.config.apiKey;\n\n let response = await fetch(`${this.baseUrl}/graphql`, {\n method: 'POST',\n headers: {\n Authorization: authHeader,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n query,\n variables,\n }),\n });\n\n // Update rate limit state from response\n this.updateRateLimitState(response);\n\n // Handle unauthorized (e.g., expired OAuth token)\n if (\n response.status === 401 &&\n this.config.onUnauthorized &&\n allowAuthRefresh\n ) {\n try {\n const newToken = await this.config.onUnauthorized();\n // Update local config and retry once without further auth refresh\n this.config.apiKey = newToken;\n const retryHeader = this.config.useBearer\n ? `Bearer ${newToken}`\n : newToken;\n response = await fetch(`${this.baseUrl}/graphql`, {\n method: 'POST',\n headers: {\n Authorization: retryHeader,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ query, variables }),\n });\n this.updateRateLimitState(response);\n } catch (e: unknown) {\n // Fall through to standard error handling\n }\n }\n\n // Handle rate limiting with exponential backoff\n if (response.status === 429) {\n if (retries > 0) {\n const retryAfter = response.headers.get('retry-after');\n const waitTime = retryAfter ? parseInt(retryAfter, 10) * 1000 : 60000;\n logger.warn(\n `Rate limited (429), retrying in ${waitTime / 1000}s (${retries} retries left)`\n );\n this.rateLimitState.retryAfter = Date.now() + waitTime;\n await this.sleep(waitTime);\n return this.graphql<T>(query, variables, retries - 1, allowAuthRefresh);\n }\n throw new Error('Linear API rate limit exceeded after retries');\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n logger.error(\n 'Linear API error response:',\n new Error(`${response.status}: ${errorText}`)\n );\n throw new Error(\n `Linear API error: ${response.status} ${response.statusText} - ${errorText}`\n );\n }\n\n const result = (await response.json()) as {\n data?: T;\n errors?: Array<{ message: string }>;\n };\n\n if (result.errors) {\n // Check for rate limit errors in GraphQL response\n const rateLimitError = result.errors.find(\n (e) =>\n e.message.toLowerCase().includes('rate limit') ||\n e.message.toLowerCase().includes('usage limit')\n );\n\n if (rateLimitError && retries > 0) {\n const waitTime = 60000; // Default 60s wait for GraphQL rate limit errors\n logger.warn(\n `GraphQL rate limit error, retrying in ${waitTime / 1000}s (${retries} retries left)`\n );\n this.rateLimitState.retryAfter = Date.now() + waitTime;\n await this.sleep(waitTime);\n return this.graphql<T>(query, variables, retries - 1);\n }\n\n logger.error('Linear GraphQL errors:', { errors: result.errors });\n throw new Error(`Linear GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n }\n\n /**\n * Create a new issue in Linear\n */\n async createIssue(input: LinearCreateIssueInput): Promise<LinearIssue> {\n const mutation = `\n mutation CreateIssue($input: IssueCreateInput!) {\n issueCreate(input: $input) {\n success\n issue {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n issueCreate: {\n success: boolean;\n issue: LinearIssue;\n };\n }>(mutation, { input });\n\n if (!result.issueCreate.success) {\n throw new Error('Failed to create Linear issue');\n }\n\n return result.issueCreate.issue;\n }\n\n /**\n * Update an existing Linear issue\n */\n async updateIssue(\n issueId: string,\n updates: Partial<LinearCreateIssueInput> & { stateId?: string }\n ): Promise<LinearIssue> {\n const mutation = `\n mutation UpdateIssue($id: String!, $input: IssueUpdateInput!) {\n issueUpdate(id: $id, input: $input) {\n success\n issue {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n issueUpdate: {\n success: boolean;\n issue: LinearIssue;\n };\n }>(mutation, { id: issueId, input: updates });\n\n if (!result.issueUpdate.success) {\n throw new Error(`Failed to update Linear issue ${issueId}`);\n }\n\n return result.issueUpdate.issue;\n }\n\n /**\n * Get issue by ID\n */\n async getIssue(issueId: string): Promise<LinearIssue | null> {\n const query = `\n query GetIssue($id: String!) {\n issue(id: $id) {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n `;\n\n const result = await this.graphql<{\n issue: LinearIssue | null;\n }>(query, { id: issueId });\n\n return result.issue;\n }\n\n /**\n * Search for issues by identifier (e.g., \"SM-123\")\n */\n async findIssueByIdentifier(identifier: string): Promise<LinearIssue | null> {\n const query = `\n query FindIssue($filter: IssueFilter!) {\n issues(filter: $filter, first: 1) {\n nodes {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n issues: {\n nodes: LinearIssue[];\n };\n }>(query, {\n filter: {\n number: {\n eq: parseInt(identifier.split('-')[1] || '0') || 0,\n },\n },\n });\n\n return result.issues.nodes[0] || null;\n }\n\n /**\n * Get team information\n */\n async getTeam(\n teamId?: string\n ): Promise<{ id: string; name: string; key: string }> {\n const query = teamId\n ? `\n query GetTeam($id: String!) {\n team(id: $id) {\n id\n name\n key\n }\n }\n `\n : `\n query GetTeams {\n teams(first: 1) {\n nodes {\n id\n name\n key\n }\n }\n }\n `;\n\n if (teamId) {\n const result = await this.graphql<{\n team: { id: string; name: string; key: string };\n }>(query, { id: teamId });\n if (!result.team) {\n throw new Error(`Team ${teamId} not found`);\n }\n return result.team;\n } else {\n const result = await this.graphql<{\n teams: {\n nodes: Array<{ id: string; name: string; key: string }>;\n };\n }>(query);\n\n if (result.teams.nodes.length === 0) {\n throw new Error('No teams found');\n }\n\n return result.teams.nodes[0]!;\n }\n }\n\n /**\n * Get workflow states for a team\n */\n async getWorkflowStates(teamId: string): Promise<\n Array<{\n id: string;\n name: string;\n type: 'backlog' | 'unstarted' | 'started' | 'completed' | 'cancelled';\n color: string;\n }>\n > {\n const query = `\n query GetWorkflowStates($teamId: String!) {\n team(id: $teamId) {\n states {\n nodes {\n id\n name\n type\n color\n }\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n team: {\n states: {\n nodes: Array<{\n id: string;\n name: string;\n type:\n | 'backlog'\n | 'unstarted'\n | 'started'\n | 'completed'\n | 'cancelled';\n color: string;\n }>;\n };\n };\n }>(query, { teamId });\n\n return result.team.states.nodes;\n }\n\n /**\n * Get current viewer/user information\n */\n async getViewer(): Promise<{\n id: string;\n name: string;\n email: string;\n }> {\n const query = `\n query GetViewer {\n viewer {\n id\n name\n email\n }\n }\n `;\n\n const result = await this.graphql<{\n viewer: {\n id: string;\n name: string;\n email: string;\n };\n }>(query);\n\n return result.viewer;\n }\n\n /**\n * Get all teams for the organization\n */\n async getTeams(): Promise<\n Array<{\n id: string;\n name: string;\n key: string;\n }>\n > {\n const query = `\n query GetTeams {\n teams(first: 50) {\n nodes {\n id\n name\n key\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n teams: {\n nodes: Array<{\n id: string;\n name: string;\n key: string;\n }>;\n };\n }>(query);\n\n return result.teams.nodes;\n }\n\n /**\n * Get issues with filtering options\n */\n async getIssues(options?: {\n teamId?: string;\n assigneeId?: string;\n stateType?: 'backlog' | 'unstarted' | 'started' | 'completed' | 'cancelled';\n limit?: number;\n }): Promise<LinearIssue[]> {\n const query = `\n query GetIssues($filter: IssueFilter, $first: Int!) {\n issues(filter: $filter, first: $first) {\n nodes {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n createdAt\n updatedAt\n url\n }\n }\n }\n `;\n\n const filter: Record<string, unknown> = {};\n\n if (options?.teamId) {\n filter.team = { id: { eq: options.teamId } };\n }\n\n if (options?.assigneeId) {\n filter.assignee = { id: { eq: options.assigneeId } };\n }\n\n if (options?.stateType) {\n filter.state = { type: { eq: options.stateType } };\n }\n\n const result = await this.graphql<{\n issues: {\n nodes: LinearIssue[];\n };\n }>(query, {\n filter: Object.keys(filter).length > 0 ? filter : undefined,\n first: options?.limit || 50,\n });\n\n return result.issues.nodes;\n }\n\n /**\n * Assign an issue to a user\n */\n async assignIssue(\n issueId: string,\n assigneeId: string\n ): Promise<{ success: boolean; issue?: LinearIssue }> {\n const mutation = `\n mutation AssignIssue($issueId: String!, $assigneeId: String!) {\n issueUpdate(id: $issueId, input: { assigneeId: $assigneeId }) {\n success\n issue {\n id\n identifier\n title\n assignee {\n id\n name\n }\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n issueUpdate: {\n success: boolean;\n issue?: LinearIssue;\n };\n }>(mutation, { issueId, assigneeId });\n\n return result.issueUpdate;\n }\n\n /**\n * Update issue state (e.g., move to \"In Progress\")\n */\n async updateIssueState(\n issueId: string,\n stateId: string\n ): Promise<{ success: boolean; issue?: LinearIssue }> {\n const mutation = `\n mutation UpdateIssueState($issueId: String!, $stateId: String!) {\n issueUpdate(id: $issueId, input: { stateId: $stateId }) {\n success\n issue {\n id\n identifier\n title\n state {\n id\n name\n type\n }\n }\n }\n }\n `;\n\n const result = await this.graphql<{\n issueUpdate: {\n success: boolean;\n issue?: LinearIssue;\n };\n }>(mutation, { issueId, stateId });\n\n return result.issueUpdate;\n }\n\n /**\n * Get an issue by ID with team info\n */\n async getIssueById(issueId: string): Promise<LinearIssue | null> {\n const query = `\n query GetIssue($issueId: String!) {\n issue(id: $issueId) {\n id\n identifier\n title\n description\n state {\n id\n name\n type\n }\n priority\n assignee {\n id\n name\n email\n }\n estimate\n labels {\n nodes {\n id\n name\n }\n }\n team {\n id\n name\n }\n createdAt\n updatedAt\n url\n }\n }\n `;\n\n try {\n const result = await this.graphql<{\n issue: LinearIssue & { team: { id: string; name: string } };\n }>(query, { issueId });\n return result.issue;\n } catch {\n return null;\n }\n }\n\n /**\n * Start working on an issue (assign to self and move to In Progress)\n */\n async startIssue(issueId: string): Promise<{\n success: boolean;\n issue?: LinearIssue;\n error?: string;\n }> {\n try {\n // Get current user\n const user = await this.getViewer();\n\n // Get the issue to find its team\n const issue = await this.getIssueById(issueId);\n if (!issue) {\n return { success: false, error: 'Issue not found' };\n }\n\n // Assign to self\n const assignResult = await this.assignIssue(issueId, user.id);\n if (!assignResult.success) {\n return { success: false, error: 'Failed to assign issue' };\n }\n\n // Find the \"In Progress\" or \"started\" state for this issue's team\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const teamId = (issue as any).team?.id;\n if (teamId) {\n const states = await this.getWorkflowStates(teamId);\n const inProgressState = states.find(\n (s) =>\n s.type === 'started' || s.name.toLowerCase().includes('progress')\n );\n\n if (inProgressState) {\n await this.updateIssueState(issueId, inProgressState.id);\n }\n }\n\n return { success: true, issue: assignResult.issue };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }