diff options
Diffstat (limited to 'lua/99/init.lua')
| -rw-r--r-- | lua/99/init.lua | 469 |
1 files changed, 250 insertions, 219 deletions
diff --git a/lua/99/init.lua b/lua/99/init.lua index 884250f..35ba93f 100644 --- a/lua/99/init.lua +++ b/lua/99/init.lua @@ -17,28 +17,29 @@ local Providers = require("99.providers") ---@param path_or_rule string | _99.Agents.Rule ---@return _99.Agents.Rule | string local function expand(path_or_rule) - if type(path_or_rule) == "string" then - return vim.fn.expand(path_or_rule) - end - return { - name = path_or_rule.name, - path = vim.fn.expand(path_or_rule.path), - } + if type(path_or_rule) == "string" then + return vim.fn.expand(path_or_rule) + end + return { + name = path_or_rule.name, + path = vim.fn.expand(path_or_rule.path), + } end --- @param opts _99.ops.Opts? --- @return _99.ops.Opts local function process_opts(opts) - opts = opts or {} - for i, rule in ipairs(opts.additional_rules or {}) do - local r = expand(rule) - assert( - type(r) ~= "string", - "broken configuration. additional_rules must never be a string" - ) - opts.additional_rules[i] = r - end - return opts + opts = opts or {} + for i, rule in ipairs(opts.additional_rules or {}) do + local r = expand(rule) + assert( + type(r) ~= "string", + "broken configuration. additional_rules must never be a string" + ) + opts.additional_rules[i] = r + end + + return opts end --- @class _99.Completion @@ -55,6 +56,7 @@ end --- @field md_files? string[] --- @field provider? _99.Providers.BaseProvider --- @field provider_extra_args? string[] +--- @field endpoint? string --- @field display_errors? boolean --- @field auto_add_skills? boolean --- @field completion? _99.Completion @@ -66,14 +68,13 @@ local _99_state --- @alias _99.TraceID number --- @class _99 ---- 99 is an agentic workflow that is meant to meld the current programmers ability ---- with the amazing powers of LLMs. Instead of being a replacement, its meant to ---- augment the programmer. +--- A4 is an agentic / trigger based completion workflow that is meant to meld the +--- current programmers ability with the amazing powers of LLMs. Instead of being +--- a replacement, its meant to augment the programmer. The power is the intentionality +--- of the programmer, not the clanker. --- ---- As of now, the direction of 99 is to progress into agentic programming and surfacing ---- of information. In the beginning and the original youtube video was about replacing ---- specific pieces of code. The more i use 99 the more i realize the better use is ---- through `search` and `work` +--- A4 is a fork of 99, the direction of A4 is to progress into "ghost" programming and surfacing +--- of language information to the programmer. --- --- ### Basic Setup --- ```lua @@ -213,11 +214,11 @@ local _99_state --- @field Extensions _99.Extensions --- check out Worker for cool abstraction on search and vibe local _99 = { - DEBUG = Level.DEBUG, - INFO = Level.INFO, - WARN = Level.WARN, - ERROR = Level.ERROR, - FATAL = Level.FATAL, + DEBUG = Level.DEBUG, + INFO = Level.INFO, + WARN = Level.WARN, + ERROR = Level.ERROR, + FATAL = Level.FATAL, } --- @param cb fun(context: _99.Prompt, o: _99.ops.Opts?): nil @@ -226,56 +227,56 @@ local _99 = { --- @param opts _99.ops.Opts --- @param capture_content string[] | nil local function capture_prompt(cb, name, context, opts, capture_content) - Window.capture_input(name, { - keymap = { - [":w"] = "submit", - }, - content = capture_content, + Window.capture_input(name, { + keymap = { + [":w"] = "submit", + }, + content = capture_content, - --- @param ok boolean - --- @param response string - cb = function(ok, response) - context.logger:debug( - "capture_prompt", - "success", - ok, - "response", - response - ) - if not ok then - return - end - local rules_and_names = Agents.by_name(_99_state.rules, response) - opts.additional_rules = opts.additional_rules or {} - for _, r in ipairs(rules_and_names.rules) do - table.insert(opts.additional_rules, r) - end - opts.additional_prompt = response - context.user_prompt = response - cb(context, opts) - end, - on_load = function() - Extensions.setup_buffer(_99_state) - end, - rules = _99_state.rules, - }) + --- @param ok boolean + --- @param response string + cb = function(ok, response) + context.logger:debug( + "capture_prompt", + "success", + ok, + "response", + response + ) + if not ok then + return + end + local rules_and_names = Agents.by_name(_99_state.rules, response) + opts.additional_rules = opts.additional_rules or {} + for _, r in ipairs(rules_and_names.rules) do + table.insert(opts.additional_rules, r) + end + opts.additional_prompt = response + context.user_prompt = response + cb(context, opts) + end, + on_load = function() + Extensions.setup_buffer(_99_state) + end, + rules = _99_state.rules, + }) end function _99.info() - local info = {} - _99_state:refresh_rules() - table.insert( - info, - string.format("Previous Requests: %d", _99_state.tracking:completed()) - ) - table.insert( - info, - string.format("custom rules(%d):", #(_99_state.rules.custom or {})) - ) - for _, rule in ipairs(_99_state.rules.custom or {}) do - table.insert(info, string.format("* %s", rule.name)) - end - Window.display_centered_message(info) + local info = {} + _99_state:refresh_rules() + table.insert( + info, + string.format("Previous Requests: %d", _99_state.tracking:completed()) + ) + table.insert( + info, + string.format("custom rules(%d):", #(_99_state.rules.custom or {})) + ) + for _, rule in ipairs(_99_state.rules.custom or {}) do + table.insert(info, string.format("* %s", rule.name)) + end + Window.display_centered_message(info) end -- elseif #tutorials == 1 then @@ -286,238 +287,268 @@ end --- @param context _99.Prompt function _99.open_tutorial(context) - local tutorial = context:tutorial_data() - Window.create_split(tutorial.tutorial, tutorial.buffer, { - split_direction = "vertical", - window_opts = { - wrap = true, - }, - }) + local tutorial = context:tutorial_data() + Window.create_split(tutorial.tutorial, tutorial.buffer, { + split_direction = "vertical", + window_opts = { + wrap = true, + }, + }) end function _99.open() - local requests = _99_state.tracking:successful() - local str_requests = Tracking.to_selectable_list(requests) - select_window(str_requests, function(idx) - local r = requests[idx] - assert(r:valid(), "encountered unexpected issue. malformated data") - if r.operation == "visual" then - --- TODO: this is its own work item for being able to have a global mark - --- section in which i keep track of marks for the lifetime of the - --- editor and when you close the editor, then it should lose them - print("visual not supported: i will figure this out... at some point") - elseif r.operation == "search" or r.operation == "vibe" then - _99.open_qfix_for_request(r) - elseif r.operation == "tutorial" then - _99.open_tutorial(r) - end - end) + local requests = _99_state.tracking:successful() + local str_requests = Tracking.to_selectable_list(requests) + select_window(str_requests, function(idx) + local r = requests[idx] + assert(r:valid(), "encountered unexpected issue. malformated data") + if r.operation == "visual" then + --- TODO: this is its own work item for being able to have a global mark + --- section in which i keep track of marks for the lifetime of the + --- editor and when you close the editor, then it should lose them + print( + "visual not supported: i will figure this out... at some point" + ) + elseif r.operation == "search" or r.operation == "vibe" then + _99.open_qfix_for_request(r) + elseif r.operation == "tutorial" then + _99.open_tutorial(r) + end + end) end --- @param opts? _99.ops.Opts --- @return _99.TraceID function _99.vibe(opts) - local o = process_opts(opts) - local context = Prompt.vibe(_99_state) - if o.additional_prompt then - context.user_prompt = o.additional_prompt - ops.vibe(context, o) - else - capture_prompt(ops.vibe, "Vibe", context, o) - end - return context.xid + local o = process_opts(opts) + local context = Prompt.vibe(_99_state) + if o.additional_prompt then + context.user_prompt = o.additional_prompt + ops.vibe(context, o) + else + capture_prompt(ops.vibe, "Vibe", context, o) + end + return context.xid end --- @param opts? _99.ops.SearchOpts --- @return _99.TraceID function _99.search(opts) - local o = process_opts(opts) --[[ @as _99.ops.SearchOpts ]] - local context = Prompt.search(_99_state) - if o.additional_prompt then - context.user_prompt = o.additional_prompt - ops.search(context, o) - else - capture_prompt(ops.search, "Search", context, o) - end - return context.xid + local o = process_opts(opts) --[[ @as _99.ops.SearchOpts ]] + local context = Prompt.search(_99_state) + if o.additional_prompt then + context.user_prompt = o.additional_prompt + ops.search(context, o) + else + capture_prompt(ops.search, "Search", context, o) + end + return context.xid end --- @param opts _99.ops.Opts function _99.tutorial(opts) - opts = process_opts(opts) - local context = Prompt.tutorial(_99_state) - if opts.additional_prompt then - context.user_prompt = opts.additional_prompt - ops.tutorial(context, opts) - else - capture_prompt(ops.tutorial, "Tutorial", context, opts) - end + opts = process_opts(opts) + local context = Prompt.tutorial(_99_state) + if opts.additional_prompt then + context.user_prompt = opts.additional_prompt + ops.tutorial(context, opts) + else + capture_prompt(ops.tutorial, "Tutorial", context, opts) + end end --- @param opts _99.ops.Opts? --- @return _99.TraceID function _99.visual(opts) - opts = process_opts(opts) - local context = Prompt.visual(_99_state) - if opts.additional_prompt then - context.user_prompt = opts.additional_prompt - ops.over_range(context, opts) - else - capture_prompt(ops.over_range, "Visual", context, opts) - end - return context.xid + opts = process_opts(opts) + local context = Prompt.visual(_99_state) --[[ @as _99.Prompt ]] + + if opts.additional_prompt then + if type(opts.additional_prompt) == "string" then + context.user_prompt = string(opts.additional_prompt) + elseif type(opts.additional_prompt) == "boolean" then + capture_prompt(ops.over_range, "Visual", context, opts) + end + else + ops.over_range(context, opts) + end + + return context.xid end function _99.view_logs() - local requests = _99_state.tracking.history - local str_requests = Tracking.to_selectable_list(requests) - select_window(str_requests, function(idx) - local r = requests[idx] - local logs = Logger.logs_by_id(r.xid) - if logs == nil then - logs = { "No logs found for request: " .. r.xid } - end - Window.display_full_screen_message(logs) - end) + local requests = _99_state.tracking.history + local str_requests = Tracking.to_selectable_list(requests) + select_window(str_requests, function(idx) + local r = requests[idx] + local logs = Logger.logs_by_id(r.xid) + if logs == nil then + logs = { "No logs found for request: " .. r.xid } + end + Window.display_full_screen_message(logs) + end) end --- @param request _99.Prompt function _99.open_qfix_for_request(request) - local items = request:qfix_data() - if #items == 0 then - print("there are no quickfix items to show") - return - end + local items = request:qfix_data() + if #items == 0 then + print("there are no quickfix items to show") + return + end - vim.fn.setqflist({}, "r", { title = "99 Results", items = items }) - vim.cmd("copen") + vim.fn.setqflist({}, "r", { title = "99 Results", items = items }) + vim.cmd("copen") end function _99.stop_all_requests() - _99_state.tracking:stop_all_requests() + _99_state.tracking:stop_all_requests() end function _99.clear_previous_requests() - _99_state.tracking:clear_history() + _99_state.tracking:clear_history() end --- if you touch this function you will be fired --- @return _99.State function _99.__get_state() - return _99_state + return _99_state end --- @param opts _99.Options? function _99.setup(opts) - opts = opts or {} + opts = opts or {} - _99_state = State.new(opts) + _99_state = State.new(opts) - local crules = _99_state.completion.custom_rules - for i, rule in ipairs(crules) do - local str = expand(rule) - assert(type(str) == "string", "error parsing rule: path must be a string") - crules[i] = str - end + local crules = _99_state.completion.custom_rules + for i, rule in ipairs(crules) do + local str = expand(rule) + assert( + type(str) == "string", + "error parsing rule: path must be a string" + ) + crules[i] = str + end - vim.api.nvim_create_autocmd("VimLeavePre", { - callback = function() - _99.stop_all_requests() - _99_state:sync() - end, - }) + vim.api.nvim_create_autocmd("VimLeavePre", { + callback = function() + _99.stop_all_requests() + _99_state:sync() + end, + }) - Logger:configure(opts.logger) + Logger:configure(opts.logger) - if opts.model then - assert(type(opts.model) == "string", "opts.model is not a string") - _99_state.model = opts.model - else - local provider = opts.provider or Providers.OpenCodeProvider - if provider._get_default_model then - _99_state.model = provider._get_default_model() + if opts.model then + assert(type(opts.model) == "string", "opts.model is not a string") + _99_state.model = opts.model + else + local provider = opts.provider or Providers.BareMetalProvider + if provider._get_default_model then + _99_state.model = provider._get_default_model() + end end - end - if opts.provider_extra_args then - assert( - type(opts.provider_extra_args) == "table", - "opts.provider_extra_args must be a table" - ) - end + if opts.endpoint then + if type(opts.endpoint) ~= "string" then + vim.notify( + "config error: opts.endpoint must be a string", + vim.log.levels.ERROR + ) + return false + end - if opts.md_files then - assert(type(opts.md_files) == "table", "opts.md_files is not a table") - for _, md in ipairs(opts.md_files) do - _99.add_md_file(md) + _99_state.endpoint = opts.endpoint + else + if opts.provider == _99.Providers.BareMetalProvider then + vim.notify( + "config error: opts.endpoint must be set for BareMetalProvider", + vim.log.levels.ERROR + ) + return false + end end - end - if opts.tmp_dir then - assert(type(opts.tmp_dir) == "string", "opts.tmp_dir must be a string") - end - _99_state.__tmp_dir = opts.tmp_dir + if opts.provider_extra_args then + assert( + type(opts.provider_extra_args) == "table", + "opts.provider_extra_args must be a table" + ) + end + + if opts.md_files then + assert(type(opts.md_files) == "table", "opts.md_files is not a table") + for _, md in ipairs(opts.md_files) do + _99.add_md_file(md) + end + end - _99_state.display_errors = opts.display_errors or false - _99_state:refresh_rules() - Extensions.init(_99_state) - Extensions.capture_project_root() + if opts.tmp_dir then + assert(type(opts.tmp_dir) == "string", "opts.tmp_dir must be a string") + end + _99_state.__tmp_dir = opts.tmp_dir - local sw = StatusWindow.new(_99_state, opts.in_flight_options) - sw:start() + _99_state.display_errors = opts.display_errors or false + _99_state:refresh_rules() + Extensions.init(_99_state) + Extensions.capture_project_root() + + local sw = StatusWindow.new(_99_state, opts.in_flight_options) + sw:start() end --- @param md string --- @return _99 function _99.add_md_file(md) - table.insert(_99_state.md_files, md) - return _99 + table.insert(_99_state.md_files, md) + return _99 end --- @param md string --- @return _99 function _99.rm_md_file(md) - for i, name in ipairs(_99_state.md_files) do - if name == md then - table.remove(_99_state.md_files, i) - break + for i, name in ipairs(_99_state.md_files) do + if name == md then + table.remove(_99_state.md_files, i) + break + end end - end - return _99 + return _99 end --- @param model string --- @return _99 function _99.set_model(model) - _99_state.model = model - return _99 + _99_state.model = model + return _99 end --- @return string function _99.get_model() - return _99_state.model + return _99_state.model end --- @return _99.Providers.BaseProvider function _99.get_provider() - return _99_state.provider_override or Providers.OpenCodeProvider + return _99_state.provider_override or Providers.OpenCodeProvider end --- @param provider _99.Providers.BaseProvider --- @return _99 function _99.set_provider(provider) - _99_state.provider_override = provider - if provider._get_default_model then - _99_state.model = provider._get_default_model() - end - return _99 + _99_state.provider_override = provider + if provider._get_default_model then + _99_state.model = provider._get_default_model() + end + return _99 end function _99.__debug() - Logger:configure({ - path = nil, - level = Level.DEBUG, - }) + Logger:configure({ + path = nil, + level = Level.DEBUG, + }) end _99.Providers = Providers @@ -525,6 +556,6 @@ _99.Providers = Providers --- @class _99.Extensions --- @field Worker _99.Extensions.Worker _99.Extensions = { - Worker = require("99.extensions.work.worker"), + Worker = require("99.extensions.work.worker"), } return _99 |
