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:
2026-06-11 12:06:17 +00:00
parent 3540bc8fa6
commit 5488008216
37 changed files with 2576 additions and 340 deletions
+82 -208
View File
@@ -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