@@ -101,11 +101,41 @@ local function get_github_token()
101101 error (' Failed to find GitHub token' )
102102end
103103
104+ local cached_gh_apps_token = nil
105+
106+ --- Get the github apps token (gho_ token)
107+ --- @return string
108+ local function get_gh_apps_token ()
109+ if cached_gh_apps_token then
110+ return cached_gh_apps_token
111+ end
112+
113+ async .util .scheduler ()
114+
115+ local config_path = utils .config_path ()
116+ if not config_path then
117+ error (' Failed to find config path for GitHub token' )
118+ end
119+
120+ local file_path = config_path .. ' /gh/hosts.yml'
121+ if vim .fn .filereadable (file_path ) == 1 then
122+ local content = table.concat (vim .fn .readfile (file_path ), ' \n ' )
123+ local token = content :match (' oauth_token:%s*([%w_]+)' )
124+ if token then
125+ cached_gh_apps_token = token
126+ return token
127+ end
128+ end
129+
130+ error (' Failed to find GitHub token' )
131+ end
132+
104133--- @type table<string , CopilotChat.Provider>
105134local M = {}
106135
107136M .copilot = {
108137 embed = ' copilot_embeddings' ,
138+ search = ' copilot_search' ,
109139
110140 get_headers = function (token )
111141 return {
@@ -279,6 +309,7 @@ M.copilot = {
279309
280310M .github_models = {
281311 embed = ' copilot_embeddings' ,
312+ search = ' copilot_search' ,
282313
283314 get_headers = function (token )
284315 return {
@@ -360,4 +391,80 @@ M.copilot_embeddings = {
360391 end ,
361392}
362393
394+ M .copilot_search = {
395+ get_headers = M .copilot .get_headers ,
396+
397+ get_token = function ()
398+ return get_gh_apps_token (), nil
399+ end ,
400+
401+ search = function (query , repository , headers )
402+ utils .curl_post (
403+ ' https://api.github.com/repos/' .. repository .. ' /copilot_internal/embeddings_index' ,
404+ {
405+ headers = headers ,
406+ }
407+ )
408+
409+ local response , err = utils .curl_get (
410+ ' https://api.github.com/repos/' .. repository .. ' /copilot_internal/embeddings_index' ,
411+ {
412+ headers = headers ,
413+ }
414+ )
415+
416+ if err then
417+ error (err )
418+ end
419+
420+ if response .status ~= 200 then
421+ error (' Failed to check search: ' .. tostring (response .status ))
422+ end
423+
424+ local body = vim .json .decode (response .body )
425+
426+ if
427+ body .can_index ~= ' ok'
428+ or not body .bm25_search_ok
429+ or not body .lexical_search_ok
430+ or not body .semantic_code_search_ok
431+ or not body .semantic_doc_search_ok
432+ or not body .semantic_indexing_enabled
433+ then
434+ error (' Failed to search: ' .. vim .inspect (body ))
435+ end
436+
437+ local body = vim .json .encode ({
438+ query = query ,
439+ scopingQuery = ' (repo:' .. repository .. ' )' ,
440+ similarity = 0.766 ,
441+ limit = 100 ,
442+ })
443+
444+ local response , err = utils .curl_post (' https://api.individual.githubcopilot.com/search/code' , {
445+ headers = headers ,
446+ body = utils .temp_file (body ),
447+ })
448+
449+ if err then
450+ error (err )
451+ end
452+
453+ if response .status ~= 200 then
454+ error (' Failed to search: ' .. tostring (response .body ))
455+ end
456+
457+ local out = {}
458+ for _ , result in ipairs (vim .json .decode (response .body )) do
459+ table.insert (out , {
460+ filename = result .path ,
461+ filetype = result .languageName :lower (),
462+ score = result .score ,
463+ content = result .contents ,
464+ })
465+ end
466+ return out
467+ end ,
468+ }
469+
363470return M
0 commit comments