summaryrefslogtreecommitdiff
path: root/lua/99/init.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/99/init.lua')
-rw-r--r--lua/99/init.lua469
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