feat: initial commit

This commit is contained in:
Barrett Ruth 2026-03-27 16:46:28 -04:00
commit c4da2cda2a
No known key found for this signature in database
GPG key ID: A6C96C9349D2FC81
23 changed files with 3743 additions and 0 deletions

316
lua/forge/codeberg.lua Normal file
View file

@ -0,0 +1,316 @@
local forge = require('forge')
---@type forge.Forge
local M = {
name = 'codeberg',
cli = 'tea',
kinds = { issue = 'issues', pr = 'pulls' },
labels = {
issue = 'Issues',
pr = 'PRs',
pr_one = 'PR',
pr_full = 'Pull Requests',
ci = 'CI/CD',
},
}
---@param kind string
---@param state string
---@return string
function M:list_cmd(kind, state)
return ('tea %s list --state %s'):format(kind, state)
end
---@param state string
---@return string[]
function M:list_pr_json_cmd(state)
return {
'tea',
'pulls',
'list',
'--state',
state,
'--output',
'json',
'--fields',
'index,title,head,state,poster,created_at',
}
end
---@param state string
---@return string[]
function M:list_issue_json_cmd(state)
return {
'tea',
'issues',
'list',
'--state',
state,
'--output',
'json',
'--fields',
'index,title,state,poster,created_at',
}
end
function M:pr_json_fields()
return {
number = 'index',
title = 'title',
branch = 'head',
state = 'state',
author = 'poster',
created_at = 'created_at',
}
end
function M:issue_json_fields()
return {
number = 'index',
title = 'title',
state = 'state',
author = 'poster',
created_at = 'created_at',
}
end
---@param kind string
---@param num string
function M:view_web(kind, num)
local base = forge.remote_web_url()
vim.ui.open(('%s/%s/%s'):format(base, kind, num))
end
---@param loc string
---@param branch string
function M:browse(loc, branch)
local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$')
vim.ui.open(('%s/src/branch/%s/%s#L%s'):format(base, branch, file, lines))
end
function M:browse_root()
vim.ui.open(forge.remote_web_url())
end
function M:browse_branch(branch)
local base = forge.remote_web_url()
vim.ui.open(base .. '/src/branch/' .. branch)
end
function M:browse_commit(sha)
local base = forge.remote_web_url()
vim.ui.open(base .. '/commit/' .. sha)
end
function M:checkout_cmd(num)
return { 'tea', 'pr', 'checkout', num }
end
---@param loc string
function M:yank_branch(loc)
local branch = vim.trim(vim.fn.system('git branch --show-current'))
local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$')
vim.fn.setreg(
'+',
('%s/src/branch/%s/%s#L%s'):format(base, branch, file, lines)
)
end
---@param loc string
function M:yank_commit(loc)
local commit = vim.trim(vim.fn.system('git rev-parse HEAD'))
local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$')
vim.fn.setreg(
'+',
('%s/src/commit/%s/%s#L%s'):format(base, commit, file, lines)
)
end
---@param num string
---@return string[]
function M:fetch_pr(num)
return { 'git', 'fetch', 'origin', ('pull/%s/head:pr-%s'):format(num, num) }
end
---@param num string
---@return string[]
function M:pr_base_cmd(num)
return { 'tea', 'pr', num, '--fields', 'base', '--output', 'simple' }
end
---@param branch string
---@return string[]
function M:pr_for_branch_cmd(_branch)
return {
'tea',
'pr',
'list',
'--fields',
'index,head',
'--output',
'simple',
'--state',
'open',
}
end
---@param num string
---@return string
function M:checks_cmd(num)
local _ = num
return 'tea actions runs list'
end
---@param run_id string
---@param failed_only boolean
---@return string[]
function M:check_log_cmd(run_id, failed_only)
local _ = failed_only
local lines = forge.config().ci.lines
return {
'sh',
'-c',
('tea actions runs logs %s | tail -n %d'):format(run_id, lines),
}
end
---@param run_id string
---@return string[]
function M:check_tail_cmd(run_id)
return { 'tea', 'actions', 'runs', 'logs', run_id, '--follow' }
end
function M:list_runs_cmd(_branch)
return 'tea actions runs list'
end
function M:run_log_cmd(id, failed_only)
local _ = failed_only
local lines = forge.config().ci.lines
return {
'sh',
'-c',
('tea actions runs logs %s | tail -n %d'):format(id, lines),
}
end
function M:run_tail_cmd(id)
return { 'tea', 'actions', 'runs', 'logs', id, '--follow' }
end
---@param num string
---@param method string
---@return string[]
function M:merge_cmd(num, method)
return { 'tea', 'pr', 'merge', num, '--style', method }
end
---@param num string
---@return string[]
function M:approve_cmd(num)
return { 'tea', 'pr', 'approve', num }
end
---@param num string
---@return string[]
function M:close_cmd(num)
return { 'tea', 'pulls', 'close', num }
end
---@param num string
---@return string[]
function M:reopen_cmd(num)
return { 'tea', 'pulls', 'reopen', num }
end
---@param num string
---@return string[]
function M:close_issue_cmd(num)
return { 'tea', 'issues', 'close', num }
end
---@param num string
---@return string[]
function M:reopen_issue_cmd(num)
return { 'tea', 'issues', 'reopen', num }
end
---@param title string
---@param body string
---@param base string
---@param _draft boolean
---@param _reviewers string[]?
---@return string[]
function M:create_pr_cmd(title, body, base, _draft, _reviewers)
return { 'tea', 'pr', 'create', '--title', title, '--description', body, '--base', base }
end
---@return string[]?
function M:create_pr_web_cmd()
local branch = vim.trim(vim.fn.system('git branch --show-current'))
local base_url = forge.remote_web_url()
local default = vim.trim(vim.fn.system(
"git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'"
))
if default == '' then
default = 'main'
end
vim.ui.open(('%s/compare/%s...%s'):format(base_url, default, branch))
return nil
end
---@return string[]
function M:default_branch_cmd()
return {
'sh', '-c',
"git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'",
}
end
---@return string[]
function M:template_paths()
return {
'.gitea/pull_request_template.md',
'.github/pull_request_template.md',
'.github/PULL_REQUEST_TEMPLATE.md',
}
end
---@param _num string
---@param _is_draft boolean
---@return string[]?
function M:draft_toggle_cmd(_num, _is_draft)
return nil
end
---@return forge.RepoInfo
function M:repo_info()
return {
permission = 'ADMIN',
merge_methods = { 'squash', 'rebase', 'merge' },
}
end
---@param num string
---@return forge.PRState
function M:pr_state(num)
local result = vim.system(
{ 'tea', 'pr', num, '--fields', 'state,mergeable', '--output', 'json' },
{ text = true }
):wait()
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
if not ok or type(data) ~= 'table' then
data = {}
end
return {
state = (data.state or 'unknown'):upper(),
mergeable = data.mergeable and 'MERGEABLE' or 'UNKNOWN',
review_decision = '',
is_draft = false,
}
end
return M

387
lua/forge/github.lua Normal file
View file

@ -0,0 +1,387 @@
local forge = require('forge')
---@type forge.Forge
local M = {
name = 'github',
cli = 'gh',
kinds = { issue = 'issue', pr = 'pr' },
labels = {
issue = 'Issues',
pr = 'PRs',
pr_one = 'PR',
pr_full = 'Pull Requests',
ci = 'CI/CD',
},
}
local function nwo()
local url = forge.remote_web_url()
return url:match('github%.com/(.+)$') or ''
end
---@param kind string
---@param state string
---@return string
function M:list_cmd(kind, state)
return ('gh %s list --limit 100 --state %s'):format(kind, state)
end
---@param state string
---@return string[]
function M:list_pr_json_cmd(state)
return {
'gh',
'pr',
'list',
'--limit',
'100',
'--state',
state,
'--json',
'number,title,headRefName,state,author,createdAt',
}
end
---@param state string
---@return string[]
function M:list_issue_json_cmd(state)
return {
'gh',
'issue',
'list',
'--limit',
'100',
'--state',
state,
'--json',
'number,title,state,author,createdAt',
}
end
function M:pr_json_fields()
return {
number = 'number',
title = 'title',
branch = 'headRefName',
state = 'state',
author = 'author',
created_at = 'createdAt',
}
end
function M:issue_json_fields()
return {
number = 'number',
title = 'title',
state = 'state',
author = 'author',
created_at = 'createdAt',
}
end
---@param kind string
---@param num string
function M:view_web(kind, num)
vim.system({ 'gh', kind, 'view', num, '--web' })
end
---@param loc string
---@param branch string
function M:browse(loc, branch)
vim.system({ 'gh', 'browse', loc, '--branch', branch })
end
function M:browse_root()
vim.system({ 'gh', 'browse' })
end
function M:browse_branch(branch)
vim.system({ 'gh', 'browse', '--branch', branch })
end
function M:browse_commit(sha)
vim.system({ 'gh', 'browse', sha })
end
function M:checkout_cmd(num)
return { 'gh', 'pr', 'checkout', num }
end
---@param loc string
function M:yank_branch(loc)
forge.yank_url({ 'gh', 'browse', loc, '-n' })
end
---@param loc string
function M:yank_commit(loc)
forge.yank_url({ 'gh', 'browse', loc, '--commit=last', '-n' })
end
---@param num string
---@return string[]
function M:fetch_pr(num)
return { 'git', 'fetch', 'origin', ('pull/%s/head:pr-%s'):format(num, num) }
end
---@param num string
---@return string[]
function M:pr_base_cmd(num)
return {
'gh',
'pr',
'view',
num,
'--json',
'baseRefName',
'--jq',
'.baseRefName',
}
end
---@param branch string
---@return string[]
function M:pr_for_branch_cmd(branch)
return {
'gh',
'pr',
'list',
'--head',
branch,
'--json',
'number',
'--jq',
'.[0].number',
}
end
---@param num string
---@return string
function M:checks_cmd(num)
return ('gh pr checks %s'):format(num)
end
---@param num string
---@return string[]
function M:checks_json_cmd(num)
return {
'gh',
'pr',
'checks',
num,
'--json',
'name,bucket,link,state,startedAt,completedAt',
}
end
---@param run_id string
---@param failed_only boolean
---@return string[]
function M:check_log_cmd(run_id, failed_only)
local lines = forge.config().ci.lines
local flag = failed_only and '--log-failed' or '--log'
return {
'sh',
'-c',
('gh run view %s -R %s %s | tail -n %d'):format(
run_id,
nwo(),
flag,
lines
),
}
end
---@param run_id string
---@return string[]
function M:check_tail_cmd(run_id)
return { 'gh', 'run', 'watch', run_id, '-R', nwo() }
end
function M:list_runs_json_cmd(branch)
local cmd = {
'gh',
'run',
'list',
'--json',
'databaseId,name,headBranch,status,conclusion,event,url,createdAt',
'--limit',
'30',
}
if branch then
table.insert(cmd, '--branch')
table.insert(cmd, branch)
end
return cmd
end
function M:normalize_run(entry)
local status = entry.status or ''
if status == 'completed' then
status = entry.conclusion or 'unknown'
end
return {
id = tostring(entry.databaseId or ''),
name = entry.name or '',
branch = entry.headBranch or '',
status = status,
event = entry.event or '',
url = entry.url or '',
created_at = entry.createdAt or '',
}
end
function M:run_log_cmd(id, failed_only)
local lines = forge.config().ci.lines
local flag = failed_only and '--log-failed' or '--log'
return {
'sh',
'-c',
('gh run view %s -R %s %s | tail -n %d'):format(id, nwo(), flag, lines),
}
end
function M:run_tail_cmd(id)
return { 'gh', 'run', 'watch', id, '-R', nwo() }
end
---@param num string
---@param method string
---@return string[]
function M:merge_cmd(num, method)
return { 'gh', 'pr', 'merge', num, '--' .. method }
end
---@param num string
---@return string[]
function M:approve_cmd(num)
return { 'gh', 'pr', 'review', num, '--approve' }
end
---@param num string
---@return string[]
function M:close_cmd(num)
return { 'gh', 'pr', 'close', num }
end
---@param num string
---@return string[]
function M:reopen_cmd(num)
return { 'gh', 'pr', 'reopen', num }
end
---@param num string
---@return string[]
function M:close_issue_cmd(num)
return { 'gh', 'issue', 'close', num }
end
---@param num string
---@return string[]
function M:reopen_issue_cmd(num)
return { 'gh', 'issue', 'reopen', num }
end
---@param title string
---@param body string
---@param base string
---@param draft boolean
---@param reviewers string[]?
---@return string[]
function M:create_pr_cmd(title, body, base, draft, reviewers)
local cmd = { 'gh', 'pr', 'create', '--title', title, '--body', body, '--base', base }
if draft then
table.insert(cmd, '--draft')
end
for _, r in ipairs(reviewers or {}) do
table.insert(cmd, '--reviewer')
table.insert(cmd, r)
end
return cmd
end
---@return string[]
function M:create_pr_web_cmd()
return { 'gh', 'pr', 'create', '--web' }
end
---@return string[]
function M:default_branch_cmd()
return { 'gh', 'repo', 'view', '--json', 'defaultBranchRef', '--jq', '.defaultBranchRef.name' }
end
---@return string[]
function M:template_paths()
return {
'.github/pull_request_template.md',
'.github/PULL_REQUEST_TEMPLATE.md',
'.github/PULL_REQUEST_TEMPLATE/',
}
end
---@param num string
---@param is_draft boolean
---@return string[]?
function M:draft_toggle_cmd(num, is_draft)
if is_draft then
return { 'gh', 'pr', 'ready', num }
end
return { 'gh', 'pr', 'ready', num, '--undo' }
end
---@return forge.RepoInfo
function M:repo_info()
local result = vim.system({
'gh',
'repo',
'view',
nwo(),
'--json',
'viewerPermission,squashMergeAllowed,rebaseMergeAllowed,mergeCommitAllowed',
}, { text = true }):wait()
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
if not ok or type(data) ~= 'table' then
data = {}
end
local methods = {}
if data.squashMergeAllowed then
table.insert(methods, 'squash')
end
if data.rebaseMergeAllowed then
table.insert(methods, 'rebase')
end
if data.mergeCommitAllowed then
table.insert(methods, 'merge')
end
return {
permission = (data.viewerPermission or 'READ'):upper(),
merge_methods = methods,
}
end
---@param num string
---@return forge.PRState
function M:pr_state(num)
local result = vim.system({
'gh',
'pr',
'view',
num,
'--json',
'state,mergeable,reviewDecision,isDraft',
}, { text = true }):wait()
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
if not ok or type(data) ~= 'table' then
data = {}
end
return {
state = data.state or 'UNKNOWN',
mergeable = data.mergeable or 'UNKNOWN',
review_decision = data.reviewDecision or '',
is_draft = data.isDraft == true,
}
end
return M

421
lua/forge/gitlab.lua Normal file
View file

@ -0,0 +1,421 @@
local forge = require('forge')
---@type forge.Forge
local M = {
name = 'gitlab',
cli = 'glab',
kinds = { issue = 'issue', pr = 'mr' },
labels = {
issue = 'Issues',
pr = 'MRs',
pr_one = 'MR',
pr_full = 'Merge Requests',
ci = 'CI/CD',
},
}
---@param kind string
---@param state string
---@return string
function M:list_cmd(kind, state)
local cmd = ('glab %s list --per-page 100'):format(kind)
if state == 'closed' then
cmd = cmd .. ' --closed'
elseif state == 'all' then
cmd = cmd .. ' --all'
end
return cmd
end
---@param state string
---@return string[]
function M:list_pr_json_cmd(state)
local cmd = {
'glab',
'mr',
'list',
'--per-page',
'100',
'--output',
'json',
}
if state == 'closed' then
table.insert(cmd, '--closed')
elseif state == 'all' then
table.insert(cmd, '--all')
end
return cmd
end
---@param state string
---@return string[]
function M:list_issue_json_cmd(state)
local cmd = {
'glab',
'issue',
'list',
'--per-page',
'100',
'--output',
'json',
}
if state == 'closed' then
table.insert(cmd, '--closed')
elseif state == 'all' then
table.insert(cmd, '--all')
end
return cmd
end
function M:pr_json_fields()
return {
number = 'iid',
title = 'title',
branch = 'source_branch',
state = 'state',
author = 'author',
created_at = 'created_at',
}
end
function M:issue_json_fields()
return {
number = 'iid',
title = 'title',
state = 'state',
author = 'author',
created_at = 'created_at',
}
end
---@param kind string
---@param num string
function M:view_web(kind, num)
vim.system({ 'glab', kind, 'view', num, '--web' })
end
---@param loc string
---@param branch string
function M:browse(loc, branch)
local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$')
vim.ui.open(('%s/-/blob/%s/%s#L%s'):format(base, branch, file, lines))
end
function M:browse_root()
vim.system({ 'glab', 'repo', 'view', '--web' })
end
function M:browse_branch(branch)
local base = forge.remote_web_url()
vim.ui.open(base .. '/-/tree/' .. branch)
end
function M:browse_commit(sha)
local base = forge.remote_web_url()
vim.ui.open(base .. '/-/commit/' .. sha)
end
function M:checkout_cmd(num)
return { 'glab', 'mr', 'checkout', num }
end
---@param loc string
function M:yank_branch(loc)
local branch = vim.trim(vim.fn.system('git branch --show-current'))
local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$')
vim.fn.setreg(
'+',
('%s/-/blob/%s/%s#L%s'):format(base, branch, file, lines)
)
end
---@param loc string
function M:yank_commit(loc)
local commit = vim.trim(vim.fn.system('git rev-parse HEAD'))
local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$')
vim.fn.setreg(
'+',
('%s/-/blob/%s/%s#L%s'):format(base, commit, file, lines)
)
end
---@param num string
---@return string[]
function M:fetch_pr(num)
return {
'git',
'fetch',
'origin',
('merge-requests/%s/head:mr-%s'):format(num, num),
}
end
---@param num string
---@return string[]
function M:pr_base_cmd(num)
return {
'sh',
'-c',
('glab mr view %s -F json | jq -r .target_branch'):format(num),
}
end
---@param branch string
---@return string[]
function M:pr_for_branch_cmd(branch)
return {
'sh',
'-c',
("glab mr list --source-branch '%s' -F json | jq -r '.[0].iid // empty'"):format(
branch
),
}
end
---@param num string
---@return string
function M:checks_cmd(num)
local _ = num
return 'glab ci list'
end
---@param run_id string
---@param failed_only boolean
---@return string[]
function M:check_log_cmd(run_id, failed_only)
local _ = failed_only
local lines = forge.config().ci.lines
return {
'sh',
'-c',
('glab ci trace %s | tail -n %d'):format(run_id, lines),
}
end
---@param run_id string
---@return string[]
function M:check_tail_cmd(run_id)
return { 'glab', 'ci', 'trace', run_id }
end
function M:list_runs_json_cmd(branch)
local cmd = {
'glab',
'ci',
'list',
'--output',
'json',
'--per-page',
'30',
}
if branch then
table.insert(cmd, '--ref')
table.insert(cmd, branch)
end
return cmd
end
function M:normalize_run(entry)
local ref = entry.ref or ''
local mr_num = ref:match('^refs/merge%-requests/(%d+)/head$')
return {
id = tostring(entry.id or ''),
name = mr_num and ('!%s'):format(mr_num) or ref,
branch = '',
status = entry.status or '',
event = entry.source or '',
url = entry.web_url or '',
created_at = entry.created_at or '',
}
end
function M:run_log_cmd(id, failed_only)
local lines = forge.config().ci.lines
local jq_filter = failed_only
and '[.[] | select(.status=="failed")][0].id // .[0].id'
or '.[0].id'
return {
'sh',
'-c',
('JOB=$(glab api \'projects/:id/pipelines/%s/jobs?per_page=100\' | jq -r \'%s\') && [ "$JOB" != "null" ] && glab ci trace "$JOB" | tail -n %d'):format(
id,
jq_filter,
lines
),
}
end
function M:run_tail_cmd(id)
local jq_filter =
'[.[] | select(.status=="running" or .status=="pending")][0].id // .[0].id'
return {
'sh',
'-c',
('JOB=$(glab api \'projects/:id/pipelines/%s/jobs?per_page=100\' | jq -r \'%s\') && [ "$JOB" != "null" ] && glab ci trace "$JOB"'):format(
id,
jq_filter
),
}
end
---@param num string
---@param method string
---@return string[]
function M:merge_cmd(num, method)
local cmd = { 'glab', 'mr', 'merge', num }
if method == 'squash' then
table.insert(cmd, '--squash')
elseif method == 'rebase' then
table.insert(cmd, '--rebase')
end
return cmd
end
---@param num string
---@return string[]
function M:approve_cmd(num)
return { 'glab', 'mr', 'approve', num }
end
---@param num string
---@return string[]
function M:close_cmd(num)
return { 'glab', 'mr', 'close', num }
end
---@param num string
---@return string[]
function M:reopen_cmd(num)
return { 'glab', 'mr', 'reopen', num }
end
---@param num string
---@return string[]
function M:close_issue_cmd(num)
return { 'glab', 'issue', 'close', num }
end
---@param num string
---@return string[]
function M:reopen_issue_cmd(num)
return { 'glab', 'issue', 'reopen', num }
end
---@param title string
---@param body string
---@param base string
---@param draft boolean
---@param reviewers string[]?
---@return string[]
function M:create_pr_cmd(title, body, base, draft, reviewers)
local cmd = {
'glab', 'mr', 'create',
'--title', title,
'--description', body,
'--target-branch', base,
'--yes',
}
if draft then
table.insert(cmd, '--draft')
end
for _, r in ipairs(reviewers or {}) do
table.insert(cmd, '--reviewer')
table.insert(cmd, r)
end
return cmd
end
---@return string[]
function M:create_pr_web_cmd()
return { 'glab', 'mr', 'create', '--web' }
end
---@return string[]
function M:default_branch_cmd()
return {
'sh', '-c',
"glab repo view -F json | jq -r '.default_branch'",
}
end
---@return string[]
function M:template_paths()
return { '.gitlab/merge_request_templates/' }
end
---@param num string
---@param is_draft boolean
---@return string[]?
function M:draft_toggle_cmd(num, is_draft)
if is_draft then
return { 'glab', 'mr', 'update', num, '--ready' }
end
return { 'glab', 'mr', 'update', num, '--draft' }
end
---@return forge.RepoInfo
function M:repo_info()
local result = vim.system(
{ 'glab', 'api', 'projects/:id' },
{ text = true }
)
:wait()
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
if not ok or type(data) ~= 'table' then
data = {}
end
local perms = type(data.permissions) == 'table' and data.permissions or {}
local pa = type(perms.project_access) == 'table' and perms.project_access
or {}
local ga = type(perms.group_access) == 'table' and perms.group_access or {}
local access = pa.access_level or 0
local group_access = ga.access_level or 0
local level = math.max(access, group_access)
local permission = 'READ'
if level >= 40 then
permission = 'ADMIN'
elseif level >= 30 then
permission = 'WRITE'
end
local methods = {}
local merge_method = data.merge_method or 'merge'
if merge_method == 'ff' or merge_method == 'rebase_merge' then
table.insert(methods, 'rebase')
else
table.insert(methods, 'merge')
end
if data.squash_option ~= 'never' then
table.insert(methods, 'squash')
end
return {
permission = permission,
merge_methods = methods,
}
end
---@param num string
---@return forge.PRState
function M:pr_state(num)
local result = vim.system(
{ 'glab', 'mr', 'view', num, '--output', 'json' },
{ text = true }
):wait()
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
if not ok or type(data) ~= 'table' then
data = {}
end
return {
state = (data.state or 'unknown'):upper(),
mergeable = data.merge_status or 'unknown',
review_decision = '',
is_draft = data.draft == true,
}
end
return M

47
lua/forge/health.lua Normal file
View file

@ -0,0 +1,47 @@
local M = {}
function M.check()
vim.health.start('forge.nvim')
if vim.fn.executable('git') == 1 then
vim.health.ok('git found')
else
vim.health.error('git not found')
end
local clis = {
{ 'gh', 'GitHub' },
{ 'glab', 'GitLab' },
{ 'tea', 'Codeberg/Gitea/Forgejo' },
}
for _, cli in ipairs(clis) do
if vim.fn.executable(cli[1]) == 1 then
vim.health.ok(cli[1] .. ' found (' .. cli[2] .. ')')
else
vim.health.info(cli[1] .. ' not found (' .. cli[2] .. ' support disabled)')
end
end
local has_fzf = pcall(require, 'fzf-lua')
if has_fzf then
vim.health.ok('fzf-lua found')
else
vim.health.error('fzf-lua not found (required)')
end
local has_diffs = pcall(require, 'diffs')
if has_diffs then
vim.health.ok('diffs.nvim found (review mode available)')
else
vim.health.info('diffs.nvim not found (review mode disabled)')
end
local has_fugitive = vim.fn.exists(':Git') == 2
if has_fugitive then
vim.health.ok('vim-fugitive found (fugitive keymaps available)')
else
vim.health.info('vim-fugitive not found (fugitive keymaps disabled)')
end
end
return M

1064
lua/forge/init.lua Normal file

File diff suppressed because it is too large Load diff