/** * GitLab tools — native pi tools over the self-hosted GitLab REST API (v4). * Thin wrappers around lib/gitlab.ts. * * CONFIGURE: config/company.json → gitHost; env.sh → GITLAB_TOKEN (scope api). * DISABLE: remove/rename this file from extensions/. */ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { mrCreate, pipelineStatus, codeSearch, deriveProjectFromOrigin } from "./lib/gitlab.ts"; export default function gitlabToolsExtension(pi: ExtensionAPI) { const register = (name: string, description: string, parameters: Record, run: (p: any) => Promise) => { pi.registerTool({ name, label: name, description, parameters: parameters as any, async execute(_id: string, params: unknown) { try { const out = await run(params ?? {}); return { content: [{ type: "text", text: JSON.stringify(out ?? {}, null, 2) }], details: {} }; } catch (err) { const msg = err instanceof Error ? err.message : String(err); return { content: [{ type: "text", text: `gitlab error: ${msg}` }], isError: true, details: {} }; } }, }); }; register( "gitlab_mr_create", "Создать merge request в GitLab. Проект определяется из git remote origin (или укажи project).", { type: "object", properties: { title: { type: "string", description: "Заголовок MR" }, sourceBranch: { type: "string", description: "Ветка-источник (по умолчанию текущая)" }, targetBranch: { type: "string", description: "Целевая ветка (по умолчанию main)" }, project: { type: "string", description: "Группа/репозиторий, например group/name. Если пусто — берётся из origin" }, removeSourceBranch: { type: "boolean", description: "Удалить ветку после мержа (default true)" }, mergeWhenPipelineSucceeds: { type: "boolean", description: "Мержить после успешного пайплайна" }, }, required: ["title"], }, async (p) => mrCreate({ title: p.title, sourceBranch: p.sourceBranch || undefined, targetBranch: p.targetBranch, project: p.project, removeSourceBranch: p.removeSourceBranch, mergeWhenPipelineSucceeds: p.mergeWhenPipelineSucceeds }), ); register( "gitlab_pipeline_status", "Показать статусы CI/CD пайплайнов проекта GitLab (по ветке).", { type: "object", properties: { project: { type: "string", description: "Группа/репозиторий (обязательно; например group/name)" }, ref: { type: "string", description: "Ветка/тег, по которому фильтровать" }, perPage: { type: "number", description: "Сколько последних пайплайнов (default 10)" }, }, required: ["project"], }, async (p) => pipelineStatus(p.project, { ref: p.ref, perPage: p.perPage }), ); register( "gitlab_code_search", "Поиск по коду в GitLab (blobs). Требует scope api/read_api у токена.", { type: "object", properties: { query: { type: "string", description: "Строка поиска (до 512 символов)" }, project: { type: "string", description: "Ограничить поиск группой/репозиторием" }, perPage: { type: "number", description: "Макс. результатов (default 20)" }, }, required: ["query"], }, async (p) => codeSearch(p.query, { project: p.project, perPage: p.perPage }), ); }