feat: Add a tool to search Linear Issues in copilot (#11518)

This PR adds a tool to search Linear issues. If the integration is
enabled for the account, the tool will return results as expected. Also
introduces support for an `active?` method, which allows third-party
Copilot tools to be conditionally enabled based on the status of the
integration on the account.
This commit is contained in:
Pranav
2025-05-19 18:00:38 -07:00
committed by GitHub
parent d657f35a76
commit 16371498ba
5 changed files with 250 additions and 17 deletions

View File

@@ -9,6 +9,8 @@ class Captain::ToolRegistryService
def register_tool(tool_class)
tool = tool_class.new(@assistant)
return unless tool.active?
@tools[tool.name] = tool
@registered_tools << tool.to_registry_format
end

View File

@@ -31,4 +31,8 @@ class Captain::Tools::BaseService
}
}
end
def active?
true
end
end

View File

@@ -0,0 +1,77 @@
class Captain::Tools::Copilot::SearchLinearIssuesService < Captain::Tools::BaseService
def name
'search_linear_issues'
end
def description
'Search Linear issues based on a search term'
end
def parameters
{
type: 'object',
properties: {
term: {
type: 'string',
description: 'The search term to find Linear issues'
}
},
required: %w[term]
}
end
def execute(arguments)
return 'Linear integration is not enabled' unless active?
term = arguments['term']
Rails.logger.info "#{self.class.name}: Service called with the search term #{term}"
return 'Missing required parameters' if term.blank?
linear_service = Integrations::Linear::ProcessorService.new(account: @assistant.account)
result = linear_service.search_issue(term)
return result[:error] if result[:error]
issues = result[:data]
return 'No issues found, I should try another similar search term' if issues.blank?
total_count = issues.length
<<~RESPONSE
Total number of issues: #{total_count}
#{issues.map { |issue| format_issue(issue) }.join("\n---\n")}
RESPONSE
end
def active?
@assistant.account.hooks.find_by(app_id: 'linear').present?
end
private
def format_issue(issue)
<<~ISSUE
Title: #{issue['title']}
ID: #{issue['id']}
State: #{issue['state']['name']}
Priority: #{format_priority(issue['priority'])}
#{issue['assignee'] ? "Assignee: #{issue['assignee']['name']}" : 'Assignee: Unassigned'}
#{issue['description'].present? ? "\nDescription: #{issue['description']}" : ''}
ISSUE
end
def format_priority(priority)
return 'No priority' if priority.nil?
case priority
when 0 then 'No priority'
when 1 then 'Urgent'
when 2 then 'High'
when 3 then 'Medium'
when 4 then 'Low'
else 'Unknown'
end
end
end