@smwb/onreel-git-mcp 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,11 @@
1
1
  import type { IssuePriority, IssueStatusCategory } from '../contract/contract.types.ts';
2
+ import type { PullRequestStatusCategory } from './pull.schema.ts';
2
3
  /** SourceCraft: системные статусы. На паузе — ещё не закончено, но и не в работе: `open`. */
3
4
  export declare const SOURCECRAFT_STATUSES: Readonly<Record<string, IssueStatusCategory>>;
4
5
  /** Категория → слаг статуса SourceCraft при переводе задачи. */
5
6
  export declare const SOURCECRAFT_STATUS_SLUGS: Readonly<Record<IssueStatusCategory, string>>;
7
+ /** Категория → слаги статусов QL фильтра списка (`status in (…)`); QL принимает только слаги, не имена. */
8
+ export declare const SOURCECRAFT_STATUS_FILTER: Readonly<Record<IssueStatusCategory, readonly string[]>>;
6
9
  /** Приоритеты SourceCraft совпадают с единой моделью по именам. */
7
10
  export declare const SOURCECRAFT_PRIORITIES: Readonly<Record<string, IssuePriority>>;
8
11
  /** Jira: `statusCategory.key` статуса. */
@@ -24,3 +27,11 @@ export declare const GITHUB_IN_PROGRESS_LABEL = "in progress";
24
27
  /** Метки GitHub для приоритета: `priority: <имя>`. */
25
28
  export declare const GITHUB_PRIORITY_LABELS: Readonly<Record<string, IssuePriority>>;
26
29
  export declare const ALL_STATUSES: readonly IssueStatusCategory[];
30
+ /** GitLab: state MR → категория; черновик — поле draft открытого MR. locked — слияние идёт, MR ещё открыт. */
31
+ export declare const GITLAB_MERGE_REQUEST_STATES: Readonly<Record<string, PullRequestStatusCategory>>;
32
+ /** GitLab: префикс заголовка, которым адаптер делает MR черновиком. */
33
+ export declare const GITLAB_DRAFT_PREFIX = "Draft: ";
34
+ /** GitLab: все формы префикса черновика, которые снимаются при переводе в open. */
35
+ export declare const GITLAB_DRAFT_PATTERN: RegExp;
36
+ /** SourceCraft: состояния PR. merging — слияние идёт, PR ещё открыт; discarded — закрыт без слияния. */
37
+ export declare const SOURCECRAFT_PULL_STATUSES: Readonly<Record<string, PullRequestStatusCategory>>;
@@ -0,0 +1,4 @@
1
+ import type { PullRequestAdapter } from './pull.types.ts';
2
+ import type { AdapterContext, TrackerDefinition } from './tracker.types.ts';
3
+ /** Адаптер PR трекера (D-005); трекер без раздела pulls — TrackerError('unsupported', 'Трекер <title> не ведёт pull request-ы.'). */
4
+ export declare function pullRequestAdapterOf(definition: TrackerDefinition, context: AdapterContext): PullRequestAdapter;
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+ /** Категория состояния PR единой модели (D-005); своё имя трекер отдаёт в status.name. */
3
+ export declare const pullRequestStatusCategorySchema: z.ZodEnum<{
4
+ closed: "closed";
5
+ draft: "draft";
6
+ merged: "merged";
7
+ open: "open";
8
+ }>;
9
+ export declare const pullRequestFilterSchema: z.ZodEnum<{
10
+ author: "author";
11
+ createdAfter: "createdAfter";
12
+ createdBefore: "createdBefore";
13
+ labels: "labels";
14
+ search: "search";
15
+ source: "source";
16
+ status: "status";
17
+ target: "target";
18
+ updatedAfter: "updatedAfter";
19
+ updatedBefore: "updatedBefore";
20
+ }>;
21
+ export declare const pullRequestSortFieldSchema: z.ZodEnum<{
22
+ created: "created";
23
+ title: "title";
24
+ updated: "updated";
25
+ }>;
26
+ export declare const mergeMethodSchema: z.ZodEnum<{
27
+ merge: "merge";
28
+ rebase: "rebase";
29
+ squash: "squash";
30
+ }>;
31
+ export declare const pullRequestSchema: z.ZodObject<{
32
+ key: z.ZodString;
33
+ title: z.ZodString;
34
+ description: z.ZodOptional<z.ZodString>;
35
+ status: z.ZodObject<{
36
+ category: z.ZodEnum<{
37
+ closed: "closed";
38
+ draft: "draft";
39
+ merged: "merged";
40
+ open: "open";
41
+ }>;
42
+ name: z.ZodString;
43
+ }, z.core.$strip>;
44
+ author: z.ZodOptional<z.ZodString>;
45
+ source: z.ZodString;
46
+ target: z.ZodString;
47
+ headSha: z.ZodOptional<z.ZodString>;
48
+ labels: z.ZodArray<z.ZodString>;
49
+ reviewers: z.ZodOptional<z.ZodArray<z.ZodString>>;
50
+ links: z.ZodArray<z.ZodObject<{
51
+ url: z.ZodString;
52
+ title: z.ZodOptional<z.ZodString>;
53
+ }, z.core.$strip>>;
54
+ createdAt: z.ZodOptional<z.ZodNumber>;
55
+ updatedAt: z.ZodOptional<z.ZodNumber>;
56
+ mergedAt: z.ZodOptional<z.ZodNumber>;
57
+ closedAt: z.ZodOptional<z.ZodNumber>;
58
+ }, z.core.$strip>;
59
+ export type PullRequestStatusCategory = z.infer<typeof pullRequestStatusCategorySchema>;
60
+ export type PullRequestFilter = z.infer<typeof pullRequestFilterSchema>;
61
+ export type PullRequestSortField = z.infer<typeof pullRequestSortFieldSchema>;
62
+ export type MergeMethod = z.infer<typeof mergeMethodSchema>;
63
+ export type PullRequest = z.infer<typeof pullRequestSchema>;
@@ -0,0 +1,74 @@
1
+ import type { IssueComment } from '../contract/contract.types.ts';
2
+ import type { MergeMethod, PullRequest, PullRequestFilter, PullRequestSortField, PullRequestStatusCategory } from './pull.schema.ts';
3
+ import type { AdapterContext, CommentCapabilities, CommentQuery, ListCapabilities, ListSort, PageQuery, TrackerPage } from './tracker.types.ts';
4
+ export type PullRequestQuery = PageQuery & {
5
+ status?: readonly PullRequestStatusCategory[] | undefined;
6
+ author?: string | undefined;
7
+ /** Ветка-источник и целевая ветка — имя без refs/heads/. */
8
+ source?: string | undefined;
9
+ target?: string | undefined;
10
+ labels?: readonly string[] | undefined;
11
+ search?: string | undefined;
12
+ createdAfter?: number | undefined;
13
+ createdBefore?: number | undefined;
14
+ updatedAfter?: number | undefined;
15
+ updatedBefore?: number | undefined;
16
+ sort?: ListSort<PullRequestSortField> | undefined;
17
+ };
18
+ export type NewPullRequestInput = {
19
+ title: string;
20
+ source: string;
21
+ target: string;
22
+ description?: string | undefined;
23
+ /** Черновиком; по умолчанию — открытый PR. */
24
+ draft?: boolean | undefined;
25
+ };
26
+ export type PullRequestPatchInput = {
27
+ title?: string | undefined;
28
+ description?: string | undefined;
29
+ target?: string | undefined;
30
+ /** open — переоткрыть или снять черновик, draft — в черновик, closed — закрыть без слияния; merged — только merge(). */
31
+ status?: Exclude<PullRequestStatusCategory, 'merged'> | undefined;
32
+ };
33
+ export type MergeInput = {
34
+ /** Нет — способ по настройке репозитория; GitHub без неё шлёт merge, GitLab при 'merge' применяет squash-настройку MR/проекта. */
35
+ method?: MergeMethod | undefined;
36
+ /** Голова источника, которую видел вызывающий: другая — отказ conflict. GitHub и GitLab сверяют её сервером
37
+ * (атомарно, нужен полный sha); у SourceCraft сверка на клиенте — лучшее старание, push между чтением и
38
+ * слиянием ей не отсекается; штатный sha — полный, в нижнем регистре. */
39
+ headSha?: string | undefined;
40
+ /** Сообщение коммита слияния; первая строка — заголовок. */
41
+ message?: string | undefined;
42
+ /** Удалить ветку-источник после слияния. */
43
+ deleteSource?: boolean | undefined;
44
+ };
45
+ export type PullRequestAdapter = {
46
+ list(query: PullRequestQuery): Promise<TrackerPage<PullRequest>>;
47
+ get(key: string): Promise<PullRequest>;
48
+ create(input: NewPullRequestInput): Promise<PullRequest>;
49
+ update(key: string, patch: PullRequestPatchInput): Promise<PullRequest>;
50
+ /** Слияние; у SourceCraft асинхронное — вернётся PR с категорией open (status.name 'open' или 'merging') до
51
+ * завершения операции, либо уже merged; операция, отклонённая трекером (failed/cancel), — отказ conflict. */
52
+ merge(key: string, input: MergeInput): Promise<PullRequest>;
53
+ comments(key: string, query: CommentQuery): Promise<TrackerPage<IssueComment>>;
54
+ comment(key: string, body: string): Promise<IssueComment>;
55
+ };
56
+ export type PullRequestCapabilities = {
57
+ list: ListCapabilities<PullRequestFilter, PullRequestSortField>;
58
+ comments: CommentCapabilities;
59
+ create: boolean;
60
+ createDraft: boolean;
61
+ /** Поля, которые меняет update. */
62
+ update: readonly ('title' | 'description' | 'target')[];
63
+ /** Категории, в которые переводит update({ status }). */
64
+ statuses: readonly Exclude<PullRequestStatusCategory, 'merged'>[];
65
+ comment: boolean;
66
+ /** Способы слияния; пусто — merge не поддерживается. */
67
+ merge: readonly MergeMethod[];
68
+ deleteSourceOnMerge: boolean;
69
+ };
70
+ /** Раздел pulls определения трекера (D-005): на том же подключении, что и задачи. */
71
+ export type PullRequestDefinition = {
72
+ capabilities: PullRequestCapabilities;
73
+ create(context: AdapterContext): PullRequestAdapter;
74
+ };
@@ -0,0 +1,2 @@
1
+ import type { PullRequestDefinition } from './pull.types.ts';
2
+ export declare function sourcecraftPulls(): PullRequestDefinition;
@@ -1,3 +1,4 @@
1
+ import type { IssueCapabilities } from './tracker.types.ts';
1
2
  /** Потолок ожидания ответа внешнего трекера, мс. */
2
3
  export declare const TRACKER_TIMEOUT_MS = 15000;
3
4
  /** Размер страницы списка задач по умолчанию; потолок — 500 по контракту. */
@@ -8,3 +9,14 @@ export declare const TRACKER_ERROR_BODY_MAX = 300;
8
9
  export declare const TRACKER_SECRETS_FILE = "tracker-secrets.json";
9
10
  /** Автор комментариев, которые пользователь пишет в локальном трекере. */
10
11
  export declare const TRACKER_LOCAL_AUTHOR = "owner";
12
+ /** Фильтры списка задач — имена полей IssueQuery; порядок — для текстов отказов. */
13
+ export declare const ISSUE_FILTERS: readonly ['status', 'labels', 'assignee', 'author', 'search', 'createdAfter', 'createdBefore', 'updatedAfter', 'updatedBefore'];
14
+ export type IssueFilter = (typeof ISSUE_FILTERS)[number];
15
+ /** Поля сортировки задач единой модели. */
16
+ export declare const ISSUE_SORT_FIELDS: readonly ['created', 'updated', 'priority', 'title', 'comments'];
17
+ export type IssueSortField = (typeof ISSUE_SORT_FIELDS)[number];
18
+ /** Фильтры списка комментариев — имена полей CommentQuery. */
19
+ export declare const COMMENT_FILTERS: readonly ['updatedAfter'];
20
+ export type CommentFilter = (typeof COMMENT_FILTERS)[number];
21
+ /** Возможности трекера без раздела issues (свои трекеры потребителя): статус и страницы, комментарии по порядку. */
22
+ export declare const LEGACY_ISSUE_CAPABILITIES: IssueCapabilities;
@@ -1,5 +1,5 @@
1
1
  import type { IssuePriority, IssueStatusCategory } from '../contract/contract.types.ts';
2
- import type { TrackerFailure } from './tracker.types.ts';
2
+ import type { CommentCapabilities, CommentQuery, IssueCapabilities, ListCapabilities, ListSort, TrackerDefinition, TrackerFailure } from './tracker.types.ts';
3
3
  /** Отказ адаптера: потребитель превращает его в ошибку своего контракта (D-353). */
4
4
  export declare class TrackerError extends Error {
5
5
  readonly failure: TrackerFailure;
@@ -19,3 +19,28 @@ export declare function requireTarget(target: string | undefined, hint: string):
19
19
  /** Обратная таблица: категория → первое имя статуса трекера. */
20
20
  export declare function nameOf(table: Readonly<Record<string, IssueStatusCategory>>, category: IssueStatusCategory): string | undefined;
21
21
  export declare function priorityName(table: Readonly<Record<string, IssuePriority>>, priority: IssuePriority): string | undefined;
22
+ /** Фильтры запроса со значением (не undefined и не пустой массив), в порядке names. */
23
+ export declare function usedFilters<F extends string>(query: Readonly<Partial<Record<F, unknown>>>, names: readonly F[]): F[];
24
+ /**
25
+ * Проверка запроса списка по возможностям (D-004, R-002): фильтр со значением вне filters или sort.field вне sorts —
26
+ * TrackerError('unsupported', `${subject}: фильтр ${name} не поддерживается.`); значение не той формы — invalid_request:
27
+ * время не числом или не целое ≥ 0, пустая строка фильтра, список со строкой-пустышкой. subject — что за список:
28
+ * 'Задачи GitHub', 'PR GitLab', 'Прогоны CI SourceCraft'.
29
+ */
30
+ export declare function assertListQuery<F extends string, S extends string>(subject: string, capabilities: ListCapabilities<F, S>, query: Readonly<Partial<Record<F, unknown>>> & {
31
+ readonly sort?: ListSort<S> | undefined;
32
+ }, names: readonly F[]): void;
33
+ /** Проверка запроса комментариев: updatedAfter вне filters или order вне capabilities.order — unsupported. */
34
+ export declare function assertCommentQuery(subject: string, capabilities: CommentCapabilities, query: CommentQuery): void;
35
+ /** Возможности задач трекера: раздел issues или LEGACY_ISSUE_CAPABILITIES. */
36
+ export declare function issueCapabilitiesOf(definition: TrackerDefinition): IssueCapabilities;
37
+ /** Мс единой модели → ISO 8601 UTC для параметров трекера. */
38
+ export declare function isoTime(ms: number): string;
39
+ /** Время записи в границах [after, before] включительно; граница задана, а время неизвестно — не проходит. */
40
+ export declare function inTimeRange(value: number | undefined, after: number | undefined, before: number | undefined): boolean;
41
+ /** Есть ли needle (без учёта регистра) подстрокой в одном из текстов. */
42
+ export declare function containsText(texts: readonly (string | undefined)[], needle: string): boolean;
43
+ /** Значение в двойных кавычках для JQL, QL SourceCraft и q поиска GitHub: экранирует \ и ". */
44
+ export declare function quoted(value: string): string;
45
+ /** Ключи таблицы сопоставления, чьё значение — одна из категорий (обратный поиск для фильтров трекера). */
46
+ export declare function keysFor<T>(table: Readonly<Record<string, T>>, values: readonly T[]): string[];
@@ -1,5 +1,7 @@
1
1
  import type { Issue, IssueComment, IssuePriority, IssueStatusCategory, TrackerDescriptor } from '../contract/contract.types.ts';
2
2
  import type { LocalIssueStore } from '../kernel/store.types.ts';
3
+ import type { PullRequestDefinition } from './pull.types.ts';
4
+ import type { CommentFilter, IssueFilter, IssueSortField } from './tracker.const.ts';
3
5
  export type TrackerFetch = (input: string, init?: {
4
6
  method?: string;
5
7
  headers?: Record<string, string>;
@@ -26,8 +28,57 @@ export type PageQuery = {
26
28
  limit: number;
27
29
  cursor?: string | undefined;
28
30
  };
31
+ export type SortDirection = 'asc' | 'desc';
32
+ /**
33
+ * Сортировка списка; нет — прежний порядок трекера: GitHub, GitLab, Jira и локальный отдают новые первыми,
34
+ * у SourceCraft свой порядок по умолчанию (старые первыми). direction по умолчанию — desc.
35
+ */
36
+ export type ListSort<F extends string> = {
37
+ field: F;
38
+ direction?: SortDirection | undefined;
39
+ };
40
+ /**
41
+ * Возможности списка (D-004): filters — фильтры, которые адаптер применяет, фильтр вне них — отказ unsupported до запроса;
42
+ * clientSide — часть filters, которую адаптер проверяет по полученной странице: страница бывает короче limit и пустой
43
+ * при nextCursor; sorts — поля сортировки, пусто — только порядок трекера.
44
+ */
45
+ export type ListCapabilities<F extends string, S extends string> = {
46
+ filters: readonly F[];
47
+ clientSide: readonly F[];
48
+ sorts: readonly S[];
49
+ };
50
+ /** Возможности списка комментариев: фильтры и допустимые порядки, первый — порядок по умолчанию. */
51
+ export type CommentCapabilities = {
52
+ filters: readonly CommentFilter[];
53
+ clientSide: readonly CommentFilter[];
54
+ order: readonly SortDirection[];
55
+ };
56
+ export type IssueCapabilities = {
57
+ list: ListCapabilities<IssueFilter, IssueSortField>;
58
+ comments: CommentCapabilities;
59
+ };
29
60
  export type IssueQuery = PageQuery & {
30
61
  status?: readonly IssueStatusCategory[] | undefined;
62
+ /** Все перечисленные метки (И). */
63
+ labels?: readonly string[] | undefined;
64
+ /** Исполнитель — как в Issue.assignee; null — задачи без исполнителя. */
65
+ assignee?: string | null | undefined;
66
+ /** Автор задачи — логин или slug трекера. */
67
+ author?: string | undefined;
68
+ /** Поиск по тексту так, как ищет трекер (у SourceCraft — префикс заголовка, у локального — подстрока). */
69
+ search?: string | undefined;
70
+ /** Границы по времени включительно, мс Unix epoch. */
71
+ createdAfter?: number | undefined;
72
+ createdBefore?: number | undefined;
73
+ updatedAfter?: number | undefined;
74
+ updatedBefore?: number | undefined;
75
+ sort?: ListSort<IssueSortField> | undefined;
76
+ };
77
+ export type CommentQuery = PageQuery & {
78
+ /** Только комментарии, изменённые не раньше момента, мс. */
79
+ updatedAfter?: number | undefined;
80
+ /** Порядок по времени создания; по умолчанию asc — как было. */
81
+ order?: SortDirection | undefined;
31
82
  };
32
83
  export type TrackerPage<T> = {
33
84
  items: T[];
@@ -54,15 +105,19 @@ export type TrackerAdapter = {
54
105
  get(key: string): Promise<Issue>;
55
106
  create(input: NewIssueInput): Promise<Issue>;
56
107
  update(key: string, patch: IssuePatchInput): Promise<Issue>;
57
- comments(key: string, query: PageQuery): Promise<TrackerPage<IssueComment>>;
108
+ comments(key: string, query: CommentQuery): Promise<TrackerPage<IssueComment>>;
58
109
  comment(key: string, body: string): Promise<IssueComment>;
59
110
  };
60
111
  /** Подключаемый трекер: описание для интерфейса и фабрика адаптера (D-350). */
61
112
  export type TrackerDefinition = {
62
113
  descriptor: TrackerDescriptor;
63
114
  create(context: AdapterContext): TrackerAdapter;
115
+ /** Фильтры и сортировка задач и комментариев; нет — LEGACY_ISSUE_CAPABILITIES. */
116
+ issues?: IssueCapabilities | undefined;
117
+ /** Pull request-ы на том же подключении (D-005); нет — трекер их не ведёт. */
118
+ pulls?: PullRequestDefinition | undefined;
64
119
  };
65
- export type TrackerFailure = 'failed' | 'unknown_issue' | 'invalid_request';
120
+ export type TrackerFailure = 'failed' | 'unknown_issue' | 'invalid_request' | 'not_found' | 'unsupported' | 'conflict' | 'rate_limited';
66
121
  export type JsonRequest = {
67
122
  fetch: TrackerFetch;
68
123
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH';
@@ -70,6 +125,13 @@ export type JsonRequest = {
70
125
  body?: unknown;
71
126
  /** 404 — «нет такой задачи», а не «неверный адрес». */
72
127
  missingIsIssue?: boolean;
128
+ /** 404 — нет этой сущности (PR, прогон, задание): код и текст отказа. Важнее missingIsIssue. */
129
+ missing?: {
130
+ failure: TrackerFailure;
131
+ message: string;
132
+ } | undefined;
133
+ /** Статусы ответа «переход сейчас невозможен» (409 отмены, 405/409/422 слияния): отказ conflict. */
134
+ conflictOn?: readonly number[] | undefined;
73
135
  };
74
136
  export type JsonResponse = {
75
137
  body: unknown;
@@ -13,8 +13,8 @@ import {
13
13
  trashRoot
14
14
  } from "../chunk-C6PHNSDC.js";
15
15
  import "../chunk-3YP22YLA.js";
16
- import "../chunk-4ZGRXIVA.js";
17
16
  import "../chunk-L7CPE2SO.js";
17
+ import "../chunk-4ZGRXIVA.js";
18
18
 
19
19
  // src/worktree/lifecycle.const.ts
20
20
  var WORKTREE_RECONCILE_INTERVAL_MS = 3e4;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smwb/onreel-git-mcp",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Библиотека модулей, выделенных из onreel: git-операции и безопасные пути, список файлов worktree, жизненный цикл worktree против порта WorktreeStore, адаптеры трекеров GitHub/GitLab/Jira/SourceCraft/локального и MCP-сервер на stdio",
5
5
  "keywords": [
6
6
  "git",