linear-rb 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95f692f69f9d45f3b6f37dc1fa41a8f5312814eabb13ebfb6ed93828a7748699
4
- data.tar.gz: 4176ed98a5f0b85cc30677622215ce39e1a1d0a4251dd5c88e61c67eb4d2df77
3
+ metadata.gz: 1da26699f5a96c66c0ab9eaff87b6d6b0af051810e289ba0fd7474d5874c2e8d
4
+ data.tar.gz: 61c18f121fc85cd46821d4291f49021cd2d2e9334e0b75ec8d54fffb4b16f840
5
5
  SHA512:
6
- metadata.gz: e3b1154891eab716f95223b77a867b1a21ada9c7336a841027db055253febc21262797a7bf17c1b7cf10ca0f9c6784d212b6b85bf81e2c3b8dcf67e794b6c27b
7
- data.tar.gz: 738eefde06923665d76b822818f93b5d1fe0c6e0dc30232c940d5fa73b2d1e4deb4010b3ea1eb0ee8115772b12e1d90dbb14fe36ac5d015121d051b447a3f05a
6
+ metadata.gz: ff8b25a4e714cc5dc5b5db510912f4eb1a157ee9f1e7289166de519797fd9a4eb1008bf4d44dc38ad16456a6a43116ade1b2b9f8893f810e12f58c358dde5efd
7
+ data.tar.gz: 2a1fa04804fa329fc0cb8d1e73c926cc08e7b290d6eadc88dff4baae898f0007f861f30fc2a0c321bb39f0de505ff95ca5f6b59f0e7fca9a6380f01cb0d08ea0
data/bin/linear CHANGED
@@ -8,21 +8,47 @@ def show_usage
8
8
  Linear CLI - Ruby wrapper for Linear GraphQL API
9
9
 
10
10
  Usage:
11
- linear issue ISSUE_ID Fetch issue details
12
- linear search QUERY [OPTIONS] Search for issues
11
+ linear issue ISSUE_ID Fetch a specific issue by ID
12
+ linear issues [OPTIONS] List and filter issues (all filters are optional)
13
13
  linear mine Show issues assigned to you
14
14
  linear teams List all teams
15
+ linear projects List all projects
15
16
  linear comment ISSUE_ID COMMENT Add a comment to an issue
16
17
  linear update ISSUE_ID STATE Update issue state
17
18
 
18
- Search Options:
19
+ Issues Filters (all optional, can be combined):
20
+ --query TEXT Search by title text
21
+ --project PROJECT_ID Filter by project ID
22
+ --state STATE Filter by state name (case-insensitive)
19
23
  --team TEAM_KEY Filter by team key
20
- --state STATE Filter by state name
21
24
 
22
25
  Examples:
26
+ # View a specific issue
23
27
  linear issue ENG-123
24
- linear search "bug fix" --team=ENG --state=Backlog
28
+
29
+ # List all issues
30
+ linear issues
31
+
32
+ # Search issues by title
33
+ linear issues --query "authentication"
34
+
35
+ # Filter by state (case-insensitive)
36
+ linear issues --state backlog
37
+ linear issues --state "In Progress"
38
+
39
+ # Filter by team
40
+ linear issues --team ENG
41
+
42
+ # Combine multiple filters
43
+ linear issues --query "bug" --state Backlog --team ENG
44
+ linear issues --project abc123 --state Done
45
+
46
+ # Your assigned issues
25
47
  linear mine
48
+
49
+ # Other commands
50
+ linear teams
51
+ linear projects
26
52
  linear comment FAT-85 "This is done"
27
53
  linear update FAT-85 "Done"
28
54
 
@@ -50,38 +76,41 @@ when 'issue'
50
76
  exit 1
51
77
  end
52
78
 
53
- when 'search'
54
- query = ARGV.shift
55
- if query.nil? || query.empty?
56
- puts "Error: search query required"
57
- puts "Usage: linear search QUERY [--team TEAM] [--state STATE]"
79
+ when 'mine'
80
+ begin
81
+ Linear::Commands.my_issues
82
+ rescue => e
83
+ puts "Error: #{e.message}"
58
84
  exit 1
59
85
  end
60
86
 
61
- options = {}
62
- OptionParser.new do |opts|
63
- opts.on("--team TEAM", "Filter by team key") { |v| options[:team] = v }
64
- opts.on("--state STATE", "Filter by state name") { |v| options[:state] = v }
65
- end.parse!
66
-
87
+ when 'teams'
67
88
  begin
68
- Linear::Commands.search(query, options)
89
+ Linear::Commands.list_teams
69
90
  rescue => e
70
91
  puts "Error: #{e.message}"
71
92
  exit 1
72
93
  end
73
94
 
74
- when 'mine'
95
+ when 'projects'
75
96
  begin
76
- Linear::Commands.my_issues
97
+ Linear::Commands.list_projects
77
98
  rescue => e
78
99
  puts "Error: #{e.message}"
79
100
  exit 1
80
101
  end
81
102
 
82
- when 'teams'
103
+ when 'issues'
104
+ options = {}
105
+ OptionParser.new do |opts|
106
+ opts.on("--query QUERY", "Filter by title text") { |v| options[:query] = v }
107
+ opts.on("--project PROJECT", "Filter by project ID") { |v| options[:project] = v }
108
+ opts.on("--state STATE", "Filter by state name") { |v| options[:state] = v }
109
+ opts.on("--team TEAM", "Filter by team key") { |v| options[:team] = v }
110
+ end.parse!
111
+
83
112
  begin
84
- Linear::Commands.list_teams
113
+ Linear::Commands.list_issues(options)
85
114
  rescue => e
86
115
  puts "Error: #{e.message}"
87
116
  exit 1
@@ -13,16 +13,18 @@ module Linear
13
13
  end
14
14
  end
15
15
 
16
- def search(query, options = {}, client: Client.new)
17
- filter = { title: { contains: query } }
16
+ def list_issues(options = {}, client: Client.new)
17
+ filter = {}
18
+ filter[:title] = { contains: options[:query] } if options[:query]
19
+ filter[:project] = { id: { eq: options[:project] } } if options[:project]
20
+ filter[:state] = { name: { eqIgnoreCase: options[:state] } } if options[:state]
18
21
  filter[:team] = { key: { eq: options[:team] } } if options[:team]
19
- filter[:state] = { name: { eq: options[:state] } } if options[:state]
20
22
 
21
- result = client.query(Queries::SEARCH_ISSUES, { filter: filter })
23
+ result = client.query(Queries::LIST_ISSUES, { filter: filter })
22
24
 
23
25
  issues = result.dig("data", "issues", "nodes") || []
24
26
  if issues.empty?
25
- puts "No issues found matching: #{query}"
27
+ puts "No issues found"
26
28
  else
27
29
  display_issue_list(issues)
28
30
  end
@@ -48,6 +50,17 @@ module Linear
48
50
  end
49
51
  end
50
52
 
53
+ def list_projects(client: Client.new)
54
+ result = client.query(Queries::PROJECTS)
55
+
56
+ projects = result.dig("data", "projects", "nodes") || []
57
+ if projects.empty?
58
+ puts "No projects found"
59
+ else
60
+ display_project_list(projects)
61
+ end
62
+ end
63
+
51
64
  def add_comment(issue_id, body, client: Client.new)
52
65
  # First get the issue to get its internal ID
53
66
  issue_result = client.query(Queries::ISSUE, { id: issue_id })
@@ -178,5 +191,30 @@ module Linear
178
191
  else "Unknown"
179
192
  end
180
193
  end
194
+
195
+ def display_project_list(projects)
196
+ puts "\nFound #{projects.length} project(s):\n\n"
197
+ projects.each do |project|
198
+ state_badge = "[#{project['state']}]".ljust(15)
199
+ progress = project['progress'] ? "#{(project['progress'] * 100).round}%" : "0%"
200
+ progress_badge = progress.ljust(6)
201
+ lead = (project.dig('lead', 'name') || 'No lead').ljust(20)
202
+
203
+ puts "#{project['name'].ljust(30)} #{state_badge} #{progress_badge} #{lead}"
204
+
205
+ if project['description'] && !project['description'].empty?
206
+ # Show first line of description
207
+ first_line = project['description'].lines.first&.strip
208
+ puts " #{first_line[0..80]}#{'...' if first_line && first_line.length > 80}" if first_line
209
+ end
210
+
211
+ if project['targetDate']
212
+ puts " Target: #{project['targetDate']}"
213
+ end
214
+
215
+ puts " URL: #{project['url']}" if project['url']
216
+ puts ""
217
+ end
218
+ end
181
219
  end
182
220
  end
@@ -23,7 +23,7 @@ module Linear
23
23
  }
24
24
  GQL
25
25
 
26
- SEARCH_ISSUES = <<~GQL
26
+ LIST_ISSUES = <<~GQL
27
27
  query($filter: IssueFilter!) {
28
28
  issues(filter: $filter) {
29
29
  nodes {
@@ -76,6 +76,27 @@ module Linear
76
76
  }
77
77
  GQL
78
78
 
79
+ PROJECTS = <<~GQL
80
+ query {
81
+ projects {
82
+ nodes {
83
+ id
84
+ name
85
+ description
86
+ state
87
+ progress
88
+ startDate
89
+ targetDate
90
+ url
91
+ lead {
92
+ name
93
+ email
94
+ }
95
+ }
96
+ }
97
+ }
98
+ GQL
99
+
79
100
  WORKFLOW_STATES = <<~GQL
80
101
  query($teamId: String!) {
81
102
  team(id: $teamId) {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linear-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Kinkead
@@ -57,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
57
  - !ruby/object:Gem::Version
58
58
  version: '0'
59
59
  requirements: []
60
- rubygems_version: 3.7.2
60
+ rubygems_version: 4.0.1
61
61
  specification_version: 4
62
62
  summary: Ruby CLI wrapper for Linear GraphQL API
63
63
  test_files: []