Files
Aleksey Shakhmatov 0c6e590273 refactor(extensions): simplify lib helpers, tool registration and kit-doctor
Behavior-preserving cleanups:
- extract shared resolveBaseUrl helper for jira/confluence base-URL logic
- extract shared registerTool helper; port jira/gitlab/confluence tools to it
- make scanSecrets declarative (filter/map)
- collapse ok/warn pair into status() and drop double existsSync in kit-doctor

Typecheck clean; npm test 71 PASS, 0 FAIL.
2026-08-07 00:24:04 +03:00

131 lines
4.6 KiB
TypeScript

/**
* Jira tools — native pi tools over the tracker REST API
* (Jira Server/DC, rest/api/2). Thin wrappers around lib/jira.ts.
*
* No MCP and no bash: these are first-class tools the agent can call directly.
* URL comes from config/company.json; JIRA_TOKEN from the env.
*
* CONFIGURE: config/company.json → trackerUrl; env.sh → JIRA_TOKEN.
* DISABLE: remove/rename this file from extensions/.
*/
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { registerTool, type ToolSpec } from "./lib/tools.ts";
import {
issueGet,
issueCreate,
issueUpdate,
issueComment,
setStatus,
search,
linkMr,
} from "./lib/jira.ts";
export default function jiraToolsExtension(pi: ExtensionAPI) {
const tools: ToolSpec[] = [
{
name: "jira_issue_get",
description: "Получить тикет Jira по ключу (summary, status, description).",
errorPrefix: "jira",
parameters: { type: "object", properties: { key: { type: "string", description: "Ключ задачи, например PROJ-123" } }, required: ["key"] },
run: async (p) => issueGet(p.key),
},
{
name: "jira_issue_create",
description: "Создать задачу в Jira.",
errorPrefix: "jira",
parameters: {
type: "object",
properties: {
projectKey: { type: "string", description: "Ключ проекта, например PROJ" },
summary: { type: "string", description: "Заголовок задачи" },
issuetype: { type: "string", description: "Тип: Bug, Task, Story и т.п." },
description: { type: "string", description: "Описание (Markdown/Plain)" },
priority: { type: "string", description: "Приоритет" },
labels: { type: "array", items: { type: "string" } },
assignee: { type: "string", description: "Исполнитель" },
},
required: ["projectKey", "summary", "issuetype"],
},
run: async (p) => issueCreate(p),
},
{
name: "jira_issue_update",
description: "Обновить поля задачи Jira (summary/description/status и т.п.). Status переводит по доступным переходам.",
errorPrefix: "jira",
parameters: {
type: "object",
properties: {
key: { type: "string", description: "Ключ задачи" },
summary: { type: "string" },
description: { type: "string" },
priority: { type: "string" },
labels: { type: "array", items: { type: "string" } },
assignee: { type: "string" },
status: { type: "string", description: "To Do / In Progress / Review / Done" },
},
required: ["key"],
},
run: async (p) => issueUpdate(p.key, p),
},
{
name: "jira_issue_comment",
description: "Добавить комментарий к тикету Jira.",
errorPrefix: "jira",
parameters: {
type: "object",
properties: {
key: { type: "string", description: "Ключ задачи" },
text: { type: "string", description: "Текст комментария" },
},
required: ["key", "text"],
},
run: async (p) => issueComment(p.key, p.text),
},
{
name: "jira_issue_transition",
description: "Перевести тикет Jira в статус (To Do / In Progress / Review / Done).",
errorPrefix: "jira",
parameters: {
type: "object",
properties: {
key: { type: "string", description: "Ключ задачи" },
target: { type: "string", description: "Целевой статус: To Do, In Progress, Review, Done" },
},
required: ["key", "target"],
},
run: async (p) => setStatus(p.key, p.target),
},
{
name: "jira_search",
description: "Найти тикеты Jira по JQL-запросу.",
errorPrefix: "jira",
parameters: {
type: "object",
properties: {
jql: { type: "string", description: "JQL, например 'project = PROJ AND status != Done'" },
maxResults: { type: "number", description: "Макс. число результатов" },
},
required: ["jql"],
},
run: async (p) => search(p.jql, p.maxResults),
},
{
name: "jira_link_mr",
description: "Привязать ссылку на merge request к тикету Jira (комментарий со ссылкой).",
errorPrefix: "jira",
parameters: {
type: "object",
properties: {
key: { type: "string", description: "Ключ задачи" },
mrUrl: { type: "string", description: "URL merge request" },
title: { type: "string", description: "Заголовок MR (по умолчанию 'Merge request')" },
},
required: ["key", "mrUrl"],
},
run: async (p) => linkMr(p.key, p.mrUrl, p.title),
},
];
for (const t of tools) registerTool(pi, t);
}