feat: add /stop and /reset chat commands and bots internals docs section
Add two new chat commands (`/stop` and `/reset`) to the Devii WebSocket handler, enabling users to stop or reset a session via text input. Introduce a new "Bots internals" documentation section with six prose pages covering architecture, personas, content generation, engagement, realism, and configuration for the autonomous bot fleet. Extend the bot service with article scoring, category picking, configurable pause/break timing, and a `gist_min_lines` parameter for LLM client initialization.
This commit is contained in:
@@ -1,47 +1,17 @@
|
||||
et nocompatible
|
||||
filetype off
|
||||
set rtp+=~/.vim/bundle/Vundle.vim
|
||||
" retoor <retoor@molodetz.nl>
|
||||
" Self-contained config for the ppy container. No external plugins or managers:
|
||||
" it works out of the box with the stock vim, and the AI edit feature uses only
|
||||
" curl and the PRAVDA_* gateway env that every instance is launched with.
|
||||
|
||||
let g:ycm_auto_trigger = 1
|
||||
|
||||
filetype plugin indent on " required
|
||||
|
||||
set clipboard=unnamedplus
|
||||
|
||||
function! GitBranch()
|
||||
let l:branch = system("bash -c 'uptime'")
|
||||
return l:branch
|
||||
endfunction
|
||||
|
||||
call plug#begin()
|
||||
" List your plugins here
|
||||
Plug 'prabirshrestha/asyncomplete.vim'
|
||||
Plug 'prabirshrestha/asyncomplete-lsp.vim'
|
||||
Plug 'tpope/vim-sensible'
|
||||
Plug 'Exafunction/codeium.vim', { 'branch': 'main' }
|
||||
Plug 'prabirshrestha/vim-lsp'
|
||||
Plug 'mattn/vim-lsp-settings'
|
||||
set statusline=\{…\}%3{codeium#GetStatusString()}\%h%m%r\ [%l,%c]\ %p%%
|
||||
highlight StatusLine guifg=#ffffff guibg=#005f87
|
||||
highlight StatusLineNC guifg=#aaaaaa guibg=#303030
|
||||
highlight ErrorMsg ctermfg=Red ctermbg=NONE guifg=#FF0000 guibg=NONE
|
||||
|
||||
call plug#end()
|
||||
|
||||
call vundle#begin()
|
||||
Plugin 'VundleVim/Vundle.vim'
|
||||
Plugin 'ycm-core/YouCompleteMe'
|
||||
call vundle#end()
|
||||
set nocompatible
|
||||
filetype plugin indent on
|
||||
syntax on
|
||||
|
||||
set encoding=utf-8
|
||||
set fileencoding=utf-8
|
||||
set termencoding=utf-8
|
||||
set mouse=a
|
||||
set backspace=indent,eol,start
|
||||
syntax on
|
||||
filetype plugin indent on
|
||||
|
||||
set showtabline=2
|
||||
set enc=utf-8
|
||||
set fenc=utf-8
|
||||
set termencoding=utf-8
|
||||
set autoindent
|
||||
set smartindent
|
||||
set tabstop=4
|
||||
@@ -49,197 +19,101 @@ set shiftwidth=4
|
||||
set expandtab
|
||||
set number
|
||||
set showmatch
|
||||
set comments=sl:/*,mb:\ *,elx:\ */
|
||||
set showtabline=2
|
||||
set laststatus=2
|
||||
set hidden
|
||||
set incsearch
|
||||
set hlsearch
|
||||
set wildmenu
|
||||
set ttimeoutlen=50
|
||||
|
||||
let mapleader = ","
|
||||
|
||||
function! ShowInTerminal(content)
|
||||
" Open a new terminal split
|
||||
belowright split | terminal
|
||||
" Wait for terminal to initialize
|
||||
call feedkeys("i") " go into insert mode to send keys
|
||||
call chansend(b:terminal_job_id, a:content . "\n")
|
||||
endfunction
|
||||
|
||||
function! AIR()
|
||||
let l:ai_prompt = input("Enter AI instructions: ")
|
||||
let $AI_PROMPT = l:ai_prompt
|
||||
let l:result = system('r ' . l:ai_prompt)
|
||||
call ShowInTerminal(l:result)
|
||||
|
||||
endfunction
|
||||
|
||||
function! AIPromptSelection()
|
||||
" Save cursor position and the unnamed register
|
||||
let l:pos = getpos('.')
|
||||
let l:save_reg = @"
|
||||
let l:save_regtype = getregtype('"')
|
||||
|
||||
" Yank the visual selection into the unnamed register
|
||||
normal! gvy
|
||||
let l:selected_text = @"
|
||||
|
||||
" Restore the unnamed register
|
||||
call setreg('"', l:save_reg, l:save_regtype)
|
||||
|
||||
" Prompt the user for AI instructions
|
||||
let l:ai_prompt = input('Enter AI instructions: ')
|
||||
let $AI_PROMPT = l:ai_prompt
|
||||
|
||||
" Call your external AI formatter (replace `c` with your command)
|
||||
let l:formatted = system('c', l:selected_text)
|
||||
|
||||
" Handle errors or replace the selection with the AI response
|
||||
if v:shell_error
|
||||
echohl ErrorMsg | echom 'Formatting failed' | echohl None
|
||||
else
|
||||
" Delete the original selection
|
||||
normal! gvd
|
||||
" Insert the formatted text *before* the cursor (same spot)
|
||||
put! =l:formatted
|
||||
" Restore the original cursor position
|
||||
call setpos('.', l:pos)
|
||||
endif
|
||||
|
||||
" Restore the unnamed register again (good measure)
|
||||
call setreg('"', l:save_reg, l:save_regtype)
|
||||
endfunction
|
||||
|
||||
" Map the AI prompt function to a key combination
|
||||
vnoremap <Leader>f :<C-u>call AIPromptSelection()<CR>
|
||||
nnoremap <Leader>r :call AIR()<CR>
|
||||
|
||||
" Existing keymappings and other configurations...
|
||||
inoremap <C-n> <ESC>:tabnext<CR>
|
||||
inoremap <C-p> <ESC>:tabnext<CR>
|
||||
nnoremap <C-n> :tabnext<CR>
|
||||
nnoremap <C-p> :tabnext<CR>
|
||||
nnoremap <Tab> :tabnext<CR>
|
||||
nnoremap <C-Tab> :tabprevious<CR>
|
||||
inoremap <C-t> <ESC>:terminal<CR><CR><C-w>r<CR><C-w>-<C-w>-<C-w>-<C-w>-<C-w>-<C-w>-<C-w>-
|
||||
nnoremap <C-e> :tabnew<Space>
|
||||
inoremap <C-e> <ESC>:tabnew<Space>
|
||||
nnoremap e :tabnew<Space>
|
||||
nnoremap q <ESC>:q<CR>
|
||||
if has('clipboard')
|
||||
set clipboard=unnamedplus
|
||||
endif
|
||||
|
||||
if !isdirectory(expand('~/.vim/undo'))
|
||||
call mkdir(expand('~/.vim/undo'), 'p')
|
||||
endif
|
||||
set undofile
|
||||
set undodir=~/.vim/undo
|
||||
|
||||
" Existing commands and other configurations...
|
||||
command! GPT py3file ~/bin/gpt
|
||||
command! Refactor py3file /home/retoor/.vim/plugin/refactor.py
|
||||
command! HelloVim py3file ~/.vim/plugin/hello.py | py3 say_hello()
|
||||
set statusline=%f\ %h%m%r\ %=\ [%{&filetype}]\ [%l,%c]\ %p%%
|
||||
highlight StatusLine cterm=bold ctermfg=15 ctermbg=24
|
||||
highlight StatusLineNC cterm=none ctermfg=250 ctermbg=236
|
||||
highlight ErrorMsg cterm=bold ctermfg=red ctermbg=none
|
||||
|
||||
if has("autocmd")
|
||||
au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
|
||||
let mapleader = ","
|
||||
|
||||
inoremap <C-n> <ESC>:tabnext<CR>
|
||||
inoremap <C-p> <ESC>:tabprevious<CR>
|
||||
nnoremap <C-n> :tabnext<CR>
|
||||
nnoremap <C-p> :tabprevious<CR>
|
||||
nnoremap <Tab> :tabnext<CR>
|
||||
nnoremap <C-Tab> :tabprevious<CR>
|
||||
nnoremap <C-e> :tabnew<Space>
|
||||
inoremap <C-e> <ESC>:tabnew<Space>
|
||||
|
||||
if has('autocmd')
|
||||
autocmd BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
|
||||
endif
|
||||
|
||||
execute pathogen#infect()
|
||||
execute pathogen#helptags()
|
||||
|
||||
" Helper: get visually selected text
|
||||
function! GetVisualSelection()
|
||||
let [line_start, col_start] = [line("'<"), col("'<")]
|
||||
let [line_end, col_end] = [line("'>"), col("'>")]
|
||||
let lines = getline(line_start, line_end)
|
||||
if len(lines) == 0
|
||||
function! s:GetVisualSelection() abort
|
||||
let [l:line_start, l:col_start] = [line("'<"), col("'<")]
|
||||
let [l:line_end, l:col_end] = [line("'>"), col("'>")]
|
||||
let l:lines = getline(l:line_start, l:line_end)
|
||||
if empty(l:lines)
|
||||
return ''
|
||||
endif
|
||||
" Trim col for first/last line if selection is characterwise
|
||||
let lines[0] = lines[0][col_start - 1 :]
|
||||
let lines[-1] = lines[-1][: col_end - (line_start == line_end ? 1 : 2)]
|
||||
return join(lines, "\n")
|
||||
let l:lines[-1] = l:lines[-1][: l:col_end - (l:line_start == l:line_end ? 1 : 2)]
|
||||
let l:lines[0] = l:lines[0][l:col_start - 1 :]
|
||||
return join(l:lines, "\n")
|
||||
endfunction
|
||||
|
||||
function! AiEditSelection()
|
||||
" (1) Get user prompt
|
||||
let l:instruction = input("AI instruction: ")
|
||||
if empty(l:instruction)
|
||||
echo "No instruction given, cancelled."
|
||||
return
|
||||
endif
|
||||
|
||||
" (2) Get visual selection
|
||||
let l:origText = GetVisualSelection()
|
||||
if empty(l:origText)
|
||||
echo "No selection."
|
||||
return
|
||||
endif
|
||||
|
||||
" (3) Compose the API prompt
|
||||
let l:prompt = l:instruction . "\n\nHere is the text:\n" . l:origText . "\n\nOutput only the transformed text. Do not include explanations, markdown, or code blocks."
|
||||
|
||||
" (4) Send to the same gateway pagent uses (PRAVDA_OPENAI_URL / PRAVDA_API_KEY)
|
||||
function! s:GatewayUrl() abort
|
||||
let l:base = substitute($PRAVDA_OPENAI_URL, '/\+$', '', '')
|
||||
if empty(l:base)
|
||||
let l:url = 'https://openai.app.molodetz.nl/v1/chat/completions'
|
||||
return 'https://openai.app.molodetz.nl/v1/chat/completions'
|
||||
elseif l:base =~# '/chat/completions$'
|
||||
let l:url = l:base
|
||||
else
|
||||
let l:url = l:base . '/chat/completions'
|
||||
return l:base
|
||||
endif
|
||||
return l:base . '/chat/completions'
|
||||
endfunction
|
||||
|
||||
function! AiEditSelection() abort
|
||||
let l:instruction = input('AI instruction: ')
|
||||
if empty(l:instruction)
|
||||
echo 'Cancelled.'
|
||||
return
|
||||
endif
|
||||
let l:orig = s:GetVisualSelection()
|
||||
if empty(l:orig)
|
||||
echo 'No selection.'
|
||||
return
|
||||
endif
|
||||
let l:prompt = l:instruction . "\n\nHere is the text:\n" . l:orig
|
||||
\ . "\n\nOutput only the transformed text. No explanations, markdown, or code blocks."
|
||||
let l:api_key = !empty($PRAVDA_API_KEY) ? $PRAVDA_API_KEY : $DEEPSEEK_API_KEY
|
||||
let l:json = '{"model":"deepseek-chat","messages":[{"role":"user","content":'.json_encode(l:prompt).'}]}'
|
||||
let l:cmd = 'curl -sS -X POST ' . shellescape(l:url) . ' -H "Authorization: Bearer ' . l:api_key . '" -H "Content-Type: application/json" -d ' . shellescape(l:json)
|
||||
let l:json = '{"model":"deepseek-chat","messages":[{"role":"user","content":' . json_encode(l:prompt) . '}]}'
|
||||
let l:cmd = 'curl -sS -X POST ' . shellescape(s:GatewayUrl())
|
||||
\ . ' -H ' . shellescape('Authorization: Bearer ' . l:api_key)
|
||||
\ . ' -H ' . shellescape('Content-Type: application/json')
|
||||
\ . ' -d ' . shellescape(l:json)
|
||||
let l:reply = system(l:cmd)
|
||||
" --- Extract output (simple JSON parsing) ---
|
||||
let l:text = matchstr(l:reply, '"content":\s*"\zs\(.\{-}\)\ze"\s*}')
|
||||
if v:shell_error
|
||||
echohl ErrorMsg | echom 'AI request failed' | echohl None
|
||||
return
|
||||
endif
|
||||
let l:text = matchstr(l:reply, '"content":\s*"\zs\(.\{-}\)\ze"\s*[,}]')
|
||||
if empty(l:text)
|
||||
echohl ErrorMsg | echom 'No content in AI response' | echohl None
|
||||
return
|
||||
endif
|
||||
let l:text = substitute(l:text, '\\n', "\n", 'g')
|
||||
let l:text = substitute(l:text, '\\"', '"', 'g')
|
||||
|
||||
" (5) Replace selected text
|
||||
let l:text = substitute(l:text, '\\t', "\t", 'g')
|
||||
normal! gv
|
||||
" Use c to change or d (delete and then 'put') depending on visual mode
|
||||
normal! c
|
||||
call feedkeys(l:text, 'n')
|
||||
endfunction
|
||||
|
||||
" Visual mode mapping
|
||||
xnoremap <silent> <Leader>a :<C-u>call AiEditSelection()<CR>
|
||||
|
||||
command! GreetPython py3 greet()
|
||||
vnoremap <leader>gr :GPTRefactor<CR>
|
||||
vnoremap <leader>gd :GPTRefactorDefault<CR>
|
||||
|
||||
|
||||
if executable('pylsp')
|
||||
" pip install python-lsp-server
|
||||
au User lsp_setup call lsp#register_server({
|
||||
\ 'name': 'pylsp',
|
||||
\ 'cmd': {server_info->['pylsp']},
|
||||
\ 'allowlist': ['python'],
|
||||
\ })
|
||||
endif
|
||||
|
||||
function! s:on_lsp_buffer_enabled() abort
|
||||
setlocal omnifunc=lsp#complete
|
||||
setlocal signcolumn=yes
|
||||
if exists('+tagfunc') | setlocal tagfunc=lsp#tagfunc | endif
|
||||
nmap <buffer> gd <plug>(lsp-definition)
|
||||
nmap <buffer> gs <plug>(lsp-document-symbol-search)
|
||||
nmap <buffer> gS <plug>(lsp-workspace-symbol-search)
|
||||
nmap <buffer> gr <plug>(lsp-references)
|
||||
nmap <buffer> gi <plug>(lsp-implementation)
|
||||
nmap <buffer> gt <plug>(lsp-type-definition)
|
||||
nmap <buffer> <leader>rn <plug>(lsp-rename)
|
||||
nmap <buffer> [g <plug>(lsp-previous-diagnostic)
|
||||
nmap <buffer> ]g <plug>(lsp-next-diagnostic)
|
||||
nmap <buffer> K <plug>(lsp-hover)
|
||||
nnoremap <buffer> <expr><c-f> lsp#scroll(+4)
|
||||
nnoremap <buffer> <expr><c-d> lsp#scroll(-4)
|
||||
|
||||
let g:lsp_format_sync_timeout = 1000
|
||||
autocmd! BufWritePre *.rs,*.go call execute('LspDocumentFormatSync')
|
||||
|
||||
" refer to doc to add more commands
|
||||
endfunction
|
||||
|
||||
augroup lsp_install
|
||||
au!
|
||||
" call s:on_lsp_buffer_enabled only for languages that has the server registered.
|
||||
autocmd User lsp_buffer_enabled call s:on_lsp_buffer_enabled()
|
||||
augroup END
|
||||
|
||||
let g:lsp_document_highlight_enabled = 1
|
||||
let g:lsp_diagnostics_enabled = 0 " disable diagnostics support
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
# devplace aptroot: lets pravda (uid 1000) run apt / apt-get / dpkg with full
|
||||
# package-manager power, without real root and without sudo. It runs the real
|
||||
# tool under fakeroot, so every chown-to-root dpkg performs is virtualized and
|
||||
# every file written to disk stays owned by pravda (uid 1000). Combined with
|
||||
# pravda owning the system trees, `apt install <pkg>` just works, and nothing
|
||||
# can ever create a root-owned file on the host bind mount.
|
||||
|
||||
_dp_tool="$(basename "$0")"
|
||||
_dp_real="/usr/bin/${_dp_tool}"
|
||||
|
||||
if [ ! -x "$_dp_real" ]; then
|
||||
echo "aptroot: real ${_dp_tool} not found at ${_dp_real}" >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
case "$_dp_tool" in
|
||||
apt|apt-get)
|
||||
exec fakeroot "$_dp_real" \
|
||||
-o APT::Sandbox::User=root \
|
||||
-o Dpkg::Use-Pty=0 \
|
||||
"$@"
|
||||
;;
|
||||
*)
|
||||
exec fakeroot "$_dp_real" "$@"
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user