Skip to content

feat(linear): added Linear tool#430

Closed
sriram2k4 wants to merge 17 commits into
simstudioai:mainfrom
sriram2k4:feat/linear
Closed

feat(linear): added Linear tool#430
sriram2k4 wants to merge 17 commits into
simstudioai:mainfrom
sriram2k4:feat/linear

Conversation

@sriram2k4

@sriram2k4 sriram2k4 commented May 28, 2025

Copy link
Copy Markdown
Contributor

Description

Users can now read and write Linear issues when both a team and project are specified.

Fixes #104

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Tested by configuring the Linear integration, attempting to create and read issues, Verified that issue creation and retrieval work as expected when all required fields are provided.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • All tests pass locally and in CI (bun run test)
  • My changes generate no new warnings
  • Any dependent changes have been merged and published in downstream modules
  • I have updated version numbers as needed (if needed)
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Security Considerations:

  • My changes do not introduce any new security vulnerabilities
  • I have considered the security implications of my changes

Additional Information:

Any additional information, configuration or data that might be necessary to reproduce the issue or use the feature.

@vercel

vercel Bot commented May 28, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 29, 2025 7:28am

@vercel

vercel Bot commented May 28, 2025

Copy link
Copy Markdown

@sriram2k4 is attempting to deploy a commit to the Sim Studio Team on Vercel.

A member of the Team first needs to authorize it.

@sriram2k4 sriram2k4 changed the title Feat/linear feat(linear): added Linear tool May 28, 2025

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

Added Linear integration to enable issue management through Linear's GraphQL API, with OAuth authentication and dedicated UI components for team/project selection.

  • Added /api/tools/linear/teams and /api/tools/linear/projects endpoints with proper OAuth token refresh and error handling
  • Created LinearBlock configuration with read/write operations and required field validation for issue management
  • Implemented LinearProjectSelector and LinearTeamSelector components with loading states and error handling
  • Added Linear OAuth provider configuration with read/write scopes and GraphQL-based user info retrieval
  • Integrated Linear tools (linearReadIssuesTool and linearCreateIssueTool) with proper GraphQL queries and response transformation

💡 (2/5) Greptile learns from your feedback when you react with 👍/👎!

17 file(s) reviewed, 21 comment(s)
Edit PR Review Bot Settings | Greptile


const linearClient = new LinearClient({ accessToken })
const teamsResult = await linearClient.teams()
const teams = teamsResult.nodes.map((team: any) => ({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Type any used for team object. Consider using proper Linear SDK types for better type safety

Suggested change
const teams = teamsResult.nodes.map((team: any) => ({
const teams = teamsResult.nodes.map((team: Team) => ({

teamId: string
label?: string
disabled?: boolean
showPreview?: boolean

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: showPreview prop is defined but never used in the component

Comment on lines +37 to +59
useEffect(() => {
if (!credential || !teamId) return
setLoading(true)
fetch('/api/tools/linear/projects', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ credential, teamId }),
})
.then((res) => res.json())
.then((data) => {
if (data.error) {
setError(data.error)
setProjects([])
} else {
setProjects(data.projects)
}
})
.catch((err) => {
setError(err.message)
setProjects([])
})
.finally(() => setLoading(false))
}, [credential, teamId])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: consider using AbortController to cancel pending requests when component unmounts or dependencies change

Suggested change
useEffect(() => {
if (!credential || !teamId) return
setLoading(true)
fetch('/api/tools/linear/projects', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ credential, teamId }),
})
.then((res) => res.json())
.then((data) => {
if (data.error) {
setError(data.error)
setProjects([])
} else {
setProjects(data.projects)
}
})
.catch((err) => {
setError(err.message)
setProjects([])
})
.finally(() => setLoading(false))
}, [credential, teamId])
useEffect(() => {
if (!credential || !teamId) return
const controller = new AbortController()
setLoading(true)
fetch('/api/tools/linear/projects', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ credential, teamId }),
signal: controller.signal
})
.then((res) => res.json())
.then((data) => {
if (data.error) {
setError(data.error)
setProjects([])
} else {
setProjects(data.projects)
}
})
.catch((err) => {
if (err.name === 'AbortError') return
setError(err.message)
setProjects([])
})
.finally(() => setLoading(false))
return () => controller.abort()
}, [credential, teamId])

headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ credential, teamId }),
})
.then((res) => res.json())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: should check res.ok before calling res.json() to handle non-200 responses

Suggested change
.then((res) => res.json())
.then((res) => {
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`)
return res.json()
})

Comment thread apps/sim/app/api/tools/linear/projects/route.ts
Comment thread apps/sim/tools/linear/create_issue.ts Outdated
projectId: '',
},
},
error: data.errors[0].message,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Only returning first error message may hide important context from multiple GraphQL errors

Comment thread apps/sim/tools/linear/types.ts
Comment thread apps/sim/tools/linear/read_issues.ts Outdated
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken || ''}`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Empty string fallback for missing access token could cause silent auth failures. Should throw error if token missing.

Suggested change
Authorization: `Bearer ${params.accessToken || ''}`,
Authorization: `Bearer ${params.accessToken ?? throw new Error('Linear access token is required')}`

Comment thread apps/sim/tools/linear/read_issues.ts Outdated
return {
success: true,
output: {
issues: data.data.issues.nodes.map((issue: any) => ({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using 'any' type for issue mapping loses type safety. Consider defining proper interface for Linear API response.

Comment thread apps/sim/tools/linear/read_issues.ts Outdated
transformResponse: async (response) => {
const data = await response.json()
if (data.errors) {
return { success: false, output: { issues: [] }, error: data.errors[0].message }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Only returns first error message when multiple GraphQL errors may be present. Consider aggregating all error messages.

@waleedlatif1

Copy link
Copy Markdown
Collaborator

merged as a part of #439

waleedlatif1 added a commit that referenced this pull request Jun 30, 2026
…5288)

* fix(workflow-renderer): validate dropbox host in note embed renderer

Replace the bare url.includes('dropbox.com') check with a parsed-hostname
match so attacker-controlled hosts (dropbox.com.evil.com, evil.com/?dropbox.com)
no longer get treated as direct dropbox videos. Resolves CodeQL
js/incomplete-url-substring-sanitization (#430).

* fix(workflow-renderer): rewrite dropbox embed via parsed URL, tolerate scheme-less links

Derive the direct video URL from the parsed URL object (rewrite hostname to
dl.dropboxusercontent.com for any dropbox.com/*.dropbox.com host) instead of a
www-only string replace, and accept scheme-less links. Fixes broken embeds for
m.dropbox.com / bare-host links flagged in review.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REQUEST] Linear Tools/Block

2 participants