claw-code/.guardrails/ide/vim-plugin/autoload/guardrail.vim

342 lines
8.2 KiB
VimL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

" guardrail.vim - Core functionality for Guardrail validation
" Maintainer: TheArchitectit
" Version: 1.0.0
let s:curl_cmd = 'curl'
let s:namespace = 'guardrail'
" Initialize signs
if !hlexists('GuardrailError')
highlight GuardrailError guifg=#ff0000 ctermfg=red
endif
if !hlexists('GuardrailWarning')
highlight GuardrailWarning guifg=#ff9900 ctermfg=yellow
endif
if !hlexists('GuardrailInfo')
highlight GuardrailInfo guifg=#0099ff ctermfg=blue
endif
sign define guardrail_error text=texthl=GuardrailError
sign define guardrail_warning text=texthl=GuardrailWarning
sign define guardrail_info text= texthl=GuardrailInfo
" Configuration
function! s:GetConfig()
return {
\ 'server_url': g:guardrail_server_url,
\ 'api_key': g:guardrail_api_key,
\ 'project_slug': g:guardrail_project_slug,
\ 'enabled': g:guardrail_enabled,
\ 'severity_threshold': g:guardrail_severity_threshold
\ }
endfunction
" Check if curl is available
function! s:CurlAvailable()
if !executable(s:curl_cmd)
echohl ErrorMsg
echo 'Guardrail: curl is required but not found'
echohl None
return 0
endif
return 1
endfunction
" Test connection to MCP server
function! guardrail#TestConnection()
if !s:CurlAvailable()
return
endif
let l:config = s:GetConfig()
let l:url = l:config.server_url . '/health/ready'
let l:cmd = [s:curl_cmd, '-s', '-o', '/dev/null', '-w', '%{http_code}', shellescape(l:url)]
if !empty(l:config.api_key)
call add(l:cmd, '-H')
call add(l:cmd, shellescape('Authorization: Bearer ' . l:config.api_key))
endif
let l:result = system(join(l:cmd, ' '))
if l:result == '200'
echo 'Guardrail: Connected'
else
echohl ErrorMsg
echo 'Guardrail: Connection failed (HTTP ' . l:result . ')'
echohl None
endif
endfunction
" Validate current buffer
function! guardrail#ValidateBuffer()
if !s:CurlAvailable()
return
endif
let l:config = s:GetConfig()
if !l:config.enabled
return
endif
let l:filename = expand('%:p')
if empty(l:filename)
return
endif
let l:lines = getline(1, '$')
let l:content = join(l:lines, "\n")
let l:language = &filetype
" Clear existing diagnostics
call guardrail#ClearDiagnostics()
" Build curl command
let l:url = l:config.server_url . '/ide/validate/file'
let l:body = json_encode({
\ 'file_path': l:filename,
\ 'content': l:content,
\ 'language': l:language,
\ 'project_slug': empty(l:config.project_slug) ? v:null : l:config.project_slug
\ })
let l:cmd = [s:curl_cmd, '-s', '-X', 'POST', shellescape(l:url)]
call add(l:cmd, '-H')
call add(l:cmd, shellescape('Content-Type: application/json'))
if !empty(l:config.api_key)
call add(l:cmd, '-H')
call add(l:cmd, shellescape('Authorization: Bearer ' . l:config.api_key))
endif
call add(l:cmd, '-d')
call add(l:cmd, shellescape(l:body))
" Execute validation
let l:result = system(join(l:cmd, ' '))
if v:shell_error != 0
echohl ErrorMsg
echo 'Guardrail: Validation request failed'
echohl None
return
endif
try
let l:response = json_decode(l:result)
call s:ProcessResponse(l:response)
catch
echohl ErrorMsg
echo 'Guardrail: Failed to parse response'
echohl None
endtry
endfunction
" Validate visual selection
function! guardrail#ValidateSelection()
if !s:CurlAvailable()
return
endif
let l:config = s:GetConfig()
if !l:config.enabled
return
endif
" Get visual selection
let [l:line1, l:col1] = getpos("'<")[1:2]
let [l:line2, l:col2] = getpos("'>")[1:2]
if l:line1 == 0 || l:line2 == 0
echo 'Guardrail: No selection'
return
endif
let l:lines = getline(l:line1, l:line2)
if len(l:lines) == 0
echo 'Guardrail: No selection'
return
endif
let l:code = join(l:lines, "\n")
let l:language = &filetype
" Build curl command
let l:url = l:config.server_url . '/ide/validate/selection'
let l:body = json_encode({
\ 'code': l:code,
\ 'language': l:language
\ })
let l:cmd = [s:curl_cmd, '-s', '-X', 'POST', shellescape(l:url)]
call add(l:cmd, '-H')
call add(l:cmd, shellescape('Content-Type: application/json'))
if !empty(l:config.api_key)
call add(l:cmd, '-H')
call add(l:cmd, shellescape('Authorization: Bearer ' . l:config.api_key))
endif
call add(l:cmd, '-d')
call add(l:cmd, shellescape(l:body))
let l:result = system(join(l:cmd, ' '))
if v:shell_error != 0
echohl ErrorMsg
echo 'Guardrail: Validation request failed'
echohl None
return
endif
try
let l:response = json_decode(l:result)
call s:ProcessSelectionResponse(l:response)
catch
echohl ErrorMsg
echo 'Guardrail: Failed to parse response'
echohl None
endtry
endfunction
" Process validation response
function! s:ProcessResponse(response)
if !has_key(a:response, 'violations')
return
endif
let l:violations = a:response.violations
let l:count = len(l:violations)
if l:count == 0
echo 'Guardrail: No violations found'
return
endif
let l:config = s:GetConfig()
let l:reported = 0
for l:violation in l:violations
if s:ShouldReport(l:violation.severity, l:config.severity_threshold)
call s:PlaceSign(l:violation)
call s:AddLocation(l:violation)
let l:reported += 1
endif
endfor
if l:reported > 0
echo 'Guardrail: Found ' . l:reported . ' violation(s)'
else
echo 'Guardrail: No violations above threshold'
endif
endfunction
" Process selection validation response
function! s:ProcessSelectionResponse(response)
if !has_key(a:response, 'violations')
return
endif
let l:violations = a:response.violations
let l:count = len(l:violations)
if l:count == 0
echo 'Guardrail: Selection is valid'
return
endif
let l:messages = []
for l:violation in l:violations
call add(l:messages, '- ' . l:violation.message)
endfor
echohl WarningMsg
echo 'Guardrail: Found ' . l:count . " violation(s):\n" . join(l:messages, "\n")
echohl None
endfunction
" Check if severity should be reported
function! s:ShouldReport(severity, threshold)
let l:levels = {'info': 1, 'warning': 2, 'error': 3}
let l:sev_level = get(l:levels, a:severity, 0)
let l:thresh_level = get(l:levels, a:threshold, 1)
return l:sev_level >= l:thresh_level
endfunction
" Place sign for violation
function! s:PlaceSign(violation)
let l:line = a:violation.line
let l:severity = a:violation.severity
if l:severity == 'error'
let l:sign = 'guardrail_error'
elseif l:severity == 'warning'
let l:sign = 'guardrail_warning'
else
let l:sign = 'guardrail_info'
endif
execute 'sign place ' . line('.') . ' line=' . l:line . ' name=' . l:sign . ' buffer=' . bufnr('%')
endfunction
" Add to location list
function! s:AddLocation(violation)
let l:item = {
\ 'bufnr': bufnr('%'),
\ 'lnum': a:violation.line,
\ 'col': a:violation.column,
\ 'text': a:violation.message,
\ 'type': a:violation.severity == 'error' ? 'E' : a:violation.severity == 'warning' ? 'W' : 'I'
\ }
call setloclist(0, [l:item], 'a')
endfunction
" Clear diagnostics for current buffer
function! guardrail#ClearDiagnostics()
execute 'sign unplace * buffer=' . bufnr('%')
call setloclist(0, [], 'r')
endfunction
" Clear diagnostics for specific buffer
function! guardrail#ClearBufferDiagnostics(filename)
let l:bufnr = bufnr(a:filename)
if l:bufnr != -1
execute 'sign unplace * buffer=' . l:bufnr
endif
endfunction
" Statusline component
function! guardrail#Statusline()
let l:config = s:GetConfig()
if !l:config.enabled
return 'Guardrail: Off'
endif
return 'Guardrail: On'
endfunction
" Open configuration
function! guardrail#OpenConfig()
" Open vimrc or init.vim
if has('nvim')
edit $MYVIMRC
else
edit $VIMRC
endif
" Move to end and add template
normal! G
call append(line('$'), [
\ '',
\ '" Guardrail Configuration',
\ 'let g:guardrail_server_url = "http://localhost:8095"',
\ 'let g:guardrail_api_key = ""',
\ 'let g:guardrail_project_slug = ""',
\ 'let g:guardrail_enabled = 1',
\ 'let g:guardrail_validate_on_save = 1',
\ 'let g:guardrail_severity_threshold = "warning"'
\ ])
endfunction