feat(api-tools): native TS tools for Jira/Confluence/GitLab, drop MCP bridge

This commit is contained in:
Aleksey Shakhmatov
2026-08-06 14:43:10 +03:00
parent 035bededb0
commit 8b273c9a72
16 changed files with 925 additions and 469 deletions
+77
View File
@@ -0,0 +1,77 @@
/**
* 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<string, unknown>, run: (p: any) => Promise<unknown>) => {
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 }),
);
}