-- "что делает функция X" / "найди в коде Y" -- Reads a digest of source files in the configured codebase root, sends to LLM. local root = jarvis.memory.recall("codebase.root") if not root or root == "" then return jarvis.cmd.not_found("Сначала укажите проект.") end local phrase = (jarvis.context.phrase or "") local question = jarvis.text.strip_trigger(phrase:lower(), { "что делает функция", "вопрос по коду", "спроси код", "найди в проекте", "найди в коде", "что в коде", "объясни код", "ask the codebase", "ask the code", "what does the function", "explain the code", }) question = question:gsub("^[%s,:%.]+", ""):gsub("%s+$", "") if question == "" then return jarvis.cmd.error("Сформулируйте вопрос.") end -- Build a digest: walk depth-2, pick source files by extension, cap per-file size + total size. local EXT_OK = { rs=true, lua=true, py=true, ts=true, tsx=true, js=true, jsx=true, svelte=true, go=true, java=true, kt=true, c=true, h=true, cpp=true, hpp=true, cs=true, rb=true, php=true, sh=true, ps1=true, sql=true, toml=true, yaml=true, yml=true, json=true, md=true, } local SKIP_DIR = { [".git"]=true, ["target"]=true, ["node_modules"]=true, ["dist"]=true, ["build"]=true, ["__pycache__"]=true, [".venv"]=true, ["venv"]=true, [".idea"]=true, [".vscode"]=true, } local MAX_FILE_BYTES = 4096 -- ~1k tokens per file local MAX_TOTAL_BYTES = 50000 -- ~12k tokens total for digest local MAX_FILES = 30 local function walk(dir, depth, acc) if depth > 3 then return end local entries = jarvis.fs.list(dir) if not entries then return end for _, ent in ipairs(entries) do if #acc.files >= MAX_FILES or acc.bytes >= MAX_TOTAL_BYTES then return end local name = ent.name local full = dir .. "\\" .. name if ent.is_dir then if not SKIP_DIR[name] and not name:match("^%.") then walk(full, depth + 1, acc) end else local ext = name:match("%.([%w]+)$") if ext and EXT_OK[ext:lower()] then local content = jarvis.fs.read(full) if content then if #content > MAX_FILE_BYTES then content = content:sub(1, MAX_FILE_BYTES) .. "\n... [truncated]" end -- relative path for readability local rel = full:sub(#root + 2) table.insert(acc.files, "--- " .. rel .. " ---\n" .. content) acc.bytes = acc.bytes + #content end end end end end local acc = { files = {}, bytes = 0 } walk(root, 1, acc) if #acc.files == 0 then return jarvis.cmd.not_found("Не нашёл исходников в проекте.") end local digest = table.concat(acc.files, "\n\n") local prompt = string.format( "Ты — старший разработчик. По digest проекта ответь на вопрос пользователя кратко (3-5 предложений) на русском. Указывай файлы где уместно.\n\n=== ВОПРОС ===\n%s\n\n=== КОД ===\n%s", question, digest ) local reply = jarvis.llm({ { role = "system", content = "Ты — внимательный код-ревьюер. Отвечай по существу, без воды." }, { role = "user", content = prompt } }, { max_tokens = 400, temperature = 0.2 }) if not reply or reply == "" then return jarvis.cmd.error("Не получилось получить ответ.") end jarvis.system.notify("Codebase Q&A", reply:sub(1, 250)) return jarvis.cmd.ok(reply)