@smwb/onreel-git-mcp 0.2.0 → 0.3.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.
- package/README.md +489 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +91 -1
- package/dist/index.js.map +1 -1
- package/dist/tracker/index.js +365 -36
- package/dist/tracker/index.js.map +1 -1
- package/dist/tracker/mapping.const.d.ts +2 -0
- package/dist/tracker/tracker.const.d.ts +12 -0
- package/dist/tracker/tracker.lib.d.ts +26 -1
- package/dist/tracker/tracker.types.d.ts +61 -2
- package/package.json +15 -3
package/README.md
ADDED
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
# @smwb/onreel-git-mcp
|
|
2
|
+
|
|
3
|
+
Библиотека модулей, выделенных из [onreel](https://git.sourcecraft.dev/elfenmail/onreel) в отдельно
|
|
4
|
+
версионируемый npm-пакет:
|
|
5
|
+
|
|
6
|
+
- **git-обвязка** — запуск `git` дочерним процессом через пул с таймаутами и ограничением параллелизма,
|
|
7
|
+
операции над worktree, ветками, статусом, diff, коммитом и rebase, и **безопасные пути**
|
|
8
|
+
(проверка confinement по realpath, отказ на симлинк);
|
|
9
|
+
- **листинги** — список записей одного каталога worktree для ревью;
|
|
10
|
+
- **жизненный цикл worktree** — замки, двухфазное создание и очистка, сверка при старте, свип и корзина —
|
|
11
|
+
против портов хранения, которые реализует потребитель;
|
|
12
|
+
- **адаптеры трекеров** — единая модель задач и пять адаптеров: GitHub, GitLab, Jira, SourceCraft и локальный
|
|
13
|
+
трекер поверх хранилища потребителя;
|
|
14
|
+
- **MCP stdio-сервер** — JSON-RPC 2.0 поверх stdin/stdout, инструменты строятся из таблицы команд вызывающего.
|
|
15
|
+
|
|
16
|
+
Пакет ESM, рантайм-зависимость одна — `zod`. Публичная поверхность держится документированной здесь:
|
|
17
|
+
новый субпуть или публичный символ добавляют вместе с записью в этом README.
|
|
18
|
+
|
|
19
|
+
## Установка
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm i @smwb/onreel-git-mcp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Требуется **Node.js >= 24** (в `package.json` — `engines`) и **Linux**: код рассчитывает на маску прав
|
|
26
|
+
сессии (`umask`), групповой `SIGKILL` процесса git и чтение `/proc/<pid>/environ` в выборе принципала MCP.
|
|
27
|
+
CI и тесты идут на образе `node:24-bookworm`; в репозиторий git должен быть доступен как команда —
|
|
28
|
+
своих git-библиотек в зависимостях нет.
|
|
29
|
+
|
|
30
|
+
Импортируют по субпутям, ядро (`src/kernel/`) своего субпути не имеет — его типы достаются из тех
|
|
31
|
+
субпутей, где они нужны вызывающему.
|
|
32
|
+
|
|
33
|
+
| Субпуть | Что отдаёт | Ссылка |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `@smwb/onreel-git-mcp` | срез контракта: константы env, zod-схемы и типы (`Issue`, `Worktree`, `ContractError`), чистые помощники (`contractError`, `sessionUmask`), `VERSION` | [Контракт](#контракт) |
|
|
36
|
+
| `…/git` | `runGit`, `GitError`, управление пулом, операции над репозиторием и worktree, безопасные пути, маскирование секретов | [git](#git) |
|
|
37
|
+
| `…/files` | `listWorktreeDir`, `DirRefusal` | [files](#files) |
|
|
38
|
+
| `…/worktree` | замки, `finishPreparing`/`finishCleaning`, reconciler, корзина — против порта `WorktreeStore` | [worktree](#worktree) |
|
|
39
|
+
| `…/tracker` | типы портов, реестр, `registerBuiltinTrackers()`, пять фабрик адаптеров, файловое хранилище токенов | [tracker](#tracker) |
|
|
40
|
+
| `…/mcp` | `serveMcp`, `mcpToolSpecs`, сборщики инструментов, выбор принципала | [mcp](#mcp) |
|
|
41
|
+
| `…/package.json` | манифест пакета | [package.json](#packagejson) |
|
|
42
|
+
|
|
43
|
+
Побочных эффектов при импорте нет нигде (R-001): регистрации, пулы и фоновые циклы запускаются только
|
|
44
|
+
явными вызовами — `registerBuiltinTrackers()`, `startWorktreeReconciler()`, `serveMcp()`.
|
|
45
|
+
|
|
46
|
+
Дальше — рабочие примеры; их же компилируемая версия лежит в [`examples/`](examples) и проверяется
|
|
47
|
+
`npm run typecheck` в CI. Примеры импортируют `src/` напрямую, а не субпути пакета: в CI линтер и
|
|
48
|
+
typecheck идут до `npm run build`, когда `dist/` ещё нет. Node 24 выполняет TypeScript напрямую, поэтому
|
|
49
|
+
каждый пример запускается и без сборки: `node examples/git.lib.ts`.
|
|
50
|
+
|
|
51
|
+
## Контракт
|
|
52
|
+
|
|
53
|
+
Корневой субпуть — срез контракта onreel: константы имён переменных окружения, схемы и выведенные из них
|
|
54
|
+
типы, чистые помощники ошибок. Имена `ONREEL_*` заморожены как есть: их смена ломает interop onreel ↔ библиотека.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { ENV_TOKEN, HOST_CONTRACT_VERSION, VERSION, contractError, formatVersion } from '@smwb/onreel-git-mcp';
|
|
58
|
+
|
|
59
|
+
const version = formatVersion(HOST_CONTRACT_VERSION); // '1.14' — версия контракта host
|
|
60
|
+
console.log(`библиотека ${VERSION}, контракт host ${version}, токен читают из ${ENV_TOKEN}`);
|
|
61
|
+
|
|
62
|
+
// Ошибку собирают здесь и отдают наружу: код из каталога, непустое сообщение и обязательные nextSteps.
|
|
63
|
+
const error = contractError('not_found', 'Задачи 42 нет.', ['Откройте список задач.']);
|
|
64
|
+
console.log(error.code, error.message, error.nextSteps.join(' '));
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## git
|
|
68
|
+
|
|
69
|
+
Каждая git-команда — дочерний процесс `git`: аргументы массивом (без shell), не больше четырёх
|
|
70
|
+
одновременно, таймаут 120 с (`GIT_TIMEOUT_MS`), для `worktree add` — 600 с (`GIT_CHECKOUT_TIMEOUT_MS`),
|
|
71
|
+
вывод не больше 16 МиБ. Всё, что приходит от клиента, git получает только после `--end-of-options`
|
|
72
|
+
или уже резолвленным в sha. Окружение ребёнка очищено от унаследованных `GIT_*`, права создаваемых
|
|
73
|
+
файлов берутся из маски сессии.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import {
|
|
77
|
+
addWorktree,
|
|
78
|
+
changedFiles,
|
|
79
|
+
closeGitPool,
|
|
80
|
+
commitChanges,
|
|
81
|
+
currentBranch,
|
|
82
|
+
GitError,
|
|
83
|
+
headOf,
|
|
84
|
+
readWorktreeFile,
|
|
85
|
+
repoToplevel,
|
|
86
|
+
resolveCommit,
|
|
87
|
+
runGit,
|
|
88
|
+
} from '@smwb/onreel-git-mcp/git';
|
|
89
|
+
|
|
90
|
+
const repo = await repoToplevel('/home/user/project');
|
|
91
|
+
const branch = await currentBranch(repo); // detached HEAD — undefined
|
|
92
|
+
const baseSha = await headOf(repo);
|
|
93
|
+
|
|
94
|
+
await addWorktree(repo, '/home/user/.onreel/workspaces/project-1a2b3c/run-1/task-1', 'onreel/run-1/task-1', baseSha);
|
|
95
|
+
|
|
96
|
+
const wt = '/home/user/.onreel/workspaces/project-1a2b3c/run-1/task-1';
|
|
97
|
+
const outcome = await commitChanges(wt, '#12 Правка по задаче'); // committed: false, если коммитить нечего
|
|
98
|
+
const changes = await changedFiles(wt, baseSha); // отслеживаемые против базы; без неотслеживаемых
|
|
99
|
+
const file = await readWorktreeFile(wt, 'src/main.ts', 64 * 1024); // {size, binary, truncated, text?}
|
|
100
|
+
console.log(branch, outcome.committed, changes.length, file?.text === undefined);
|
|
101
|
+
|
|
102
|
+
// Нештатный код, таймаут или переполнение вывода — GitError.
|
|
103
|
+
const missing = await resolveCommit(repo, 'refs/heads/нет-такой-ветки'); // undefined, не ошибка
|
|
104
|
+
try {
|
|
105
|
+
await runGit(['rev-parse', '--verify', 'HEAD^1'], repo, { okCodes: [] });
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error instanceof GitError) console.log(error.exitCode, error.timedOut, error.stderr);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Остановка процесса.** `closeGitPool()` вызывает потребитель в своей точке остановки host'а: очередь
|
|
112
|
+
отклоняется, запущенные git убиваются групповым `SIGKILL`. `isGitPoolClosed()` позволяет отличить сбой
|
|
113
|
+
«пул закрыт, мы останавливаемся» от настоящей ошибки git — в последнем случае запись `failed` не пишут.
|
|
114
|
+
`reopenGitPool()` — только для тестов.
|
|
115
|
+
|
|
116
|
+
**Маскирование секретов (D-272, D-194).** `GitError` маскирует аргументы и stderr **до** обрезки, иначе
|
|
117
|
+
срез оставил бы префикс секрета, который шаблон не узнаёт; stdout маскируется при первом чтении. Шаблонов
|
|
118
|
+
R1–R8 (URL с `user:pass@`, `Authorization:`, известные ключи доступа, JWT, PEM, `onr_…`-токен, присваивания
|
|
119
|
+
вида `token = …`, пароли в CLI-флагах) бывает мало — значение, не похожее ни на один шаблон, регистрируют
|
|
120
|
+
в приёмнике секретов:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { defaultSecretSink, redactSecretText, type SecretSink } from '@smwb/onreel-git-mcp/git';
|
|
124
|
+
|
|
125
|
+
defaultSecretSink.register(process.env.MYopaqueValue ?? ''); // короче 8 символов — игнорируется
|
|
126
|
+
console.log(redactSecretText('клон https://user:секрет@git.example/o/r и MYopaqueValue'));
|
|
127
|
+
|
|
128
|
+
// Потребитель со своим журналом подставляет свой приёмник — тем же типом SecretSink.
|
|
129
|
+
const known = new Set<string>();
|
|
130
|
+
const sink: SecretSink = {
|
|
131
|
+
register(secret: string): void {
|
|
132
|
+
known.add(secret);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Значения `SecretSink`, `defaultSecretSink` и `redactSecretText` реэкспортированы из `…/git`; отдельного
|
|
138
|
+
субпути `…/kernel` у пакета нет.
|
|
139
|
+
|
|
140
|
+
## files
|
|
141
|
+
|
|
142
|
+
`listWorktreeDir` отдаёт записи одного каталога worktree: не глубже одного уровня, симлинки не разыменовываются
|
|
143
|
+
(`symlink` остаётся листом), служебный `.git` пропускается без учёта регистра, каталог и путь запроса проверяются
|
|
144
|
+
на confinement по realpath. `truncated` ставится, если просмотрели больше `scanMax` записей или обрезали по
|
|
145
|
+
`maxEntries`; имя не в UTF-8 тоже обрезает список с этим признаком.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { DirRefusal, listWorktreeDir } from '@smwb/onreel-git-mcp/files';
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const root = await listWorktreeDir(dataDir, worktreePath, undefined);
|
|
152
|
+
const nested = await listWorktreeDir(dataDir, worktreePath, 'src', { maxEntries: 200, scanMax: 1000 });
|
|
153
|
+
console.log(root.truncated, root.entries.map((entry) => `${entry.kind} ${entry.path}`), nested.entries.length);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
// Причина отказа — часть контракта: её контроллер переводит в ошибку worktree.files по матрице D-367.
|
|
156
|
+
if (error instanceof DirRefusal) console.log(error.reason); // 'outside_worktree' | 'excluded' | 'not_found' | …
|
|
157
|
+
else throw error;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## worktree
|
|
162
|
+
|
|
163
|
+
Двухфазные создание и очистка (onreel D-050): receipt-транзакция потребителя записала `preparing` или
|
|
164
|
+
`cleaning`, библиотека доводит состояние до `ready`, `cleaned` или `failed`, работая с git и диском вне
|
|
165
|
+
транзакции. Состояние читается и пишется через **порт `WorktreeStore`** — его реализует вызывающий (D-003);
|
|
166
|
+
в пакете нет ни SQLite, ни ledger, ни другой конкретной БД.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import {
|
|
170
|
+
finishPreparing,
|
|
171
|
+
isWorktreeBusy,
|
|
172
|
+
startWorktreeReconciler,
|
|
173
|
+
withWorktreeLock,
|
|
174
|
+
type WorktreeLifecycleServices,
|
|
175
|
+
type WorktreeStore,
|
|
176
|
+
} from '@smwb/onreel-git-mcp/worktree';
|
|
177
|
+
|
|
178
|
+
const services: WorktreeLifecycleServices = {
|
|
179
|
+
store: myStore satisfies WorktreeStore,
|
|
180
|
+
dataDir: '/home/user/.onreel',
|
|
181
|
+
log: (line) => console.error(line),
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
await finishPreparing(services, 'wt-9'); // preparing → ready с HEAD; сбой → failed и сразу очистка остатков
|
|
185
|
+
|
|
186
|
+
const reconciler = startWorktreeReconciler(services); // 30 с по умолчанию (WORKTREE_RECONCILE_INTERVAL_MS)
|
|
187
|
+
await reconciler.kick(); // свип вне таймера: после мержа, после stop воркера
|
|
188
|
+
await reconciler.stop();
|
|
189
|
+
|
|
190
|
+
// Замок — один на worktree в пределах процесса: создание, очистка, коммит и rebase не перекрываются.
|
|
191
|
+
console.log(isWorktreeBusy('wt-9'));
|
|
192
|
+
await withWorktreeLock('wt-9', async () => {
|
|
193
|
+
/* работа с этим worktree */
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Стартует `startWorktreeReconciler` — он же делает сверку при старте (`reconcileWorktreesAtStart`: осиротевший
|
|
198
|
+
`preparing` → `failed` и в очистку) и доочищает корзину (`emptyTrash`). `sweepWorktrees` отдельно готовит к
|
|
199
|
+
очистке `ready`-worktree завершённых задач и доводит начатые `cleaning`. Незакоммиченные изменения без явного
|
|
200
|
+
решения вызывающего (`discardChanges`) очистка не выбрасывает: worktree уходит в `failed` и ждёт решения.
|
|
201
|
+
|
|
202
|
+
Порт `WorktreeStore` раскрывает пять методов и явную единицу работы — `inTransaction(fn)`, которой вызывают
|
|
203
|
+
все переходы (`markWorktreeReady`, `markWorktreeFailed`, `requestWorktreeCleanup`, `completeWorktreeCleanup`);
|
|
204
|
+
вложенность и асинхронные обратные вызовы не поддерживаются. **Эталон реализации — `test/helpers/memory-store.lib.ts`
|
|
205
|
+
в тестовом дереве пакета** (D-003): он же образец семантики для потребителей, включая копию-на-коммит в
|
|
206
|
+
`inTransaction` и отказ броска как откат.
|
|
207
|
+
|
|
208
|
+
## tracker
|
|
209
|
+
|
|
210
|
+
Единая модель задач поверх внешних трекеров и локального хранилища. Адаптер — `TrackerAdapter` (list/get/create/
|
|
211
|
+
update/comments/comment), подключаемый трекер — `TrackerDefinition` (`descriptor` для интерфейса + фабрика).
|
|
212
|
+
`fetch` **приходит в контексте**, а не берётся из глобальной области: тесты подставляют свою функцию, потребитель —
|
|
213
|
+
свою обёртку с прокси.
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import {
|
|
217
|
+
findTracker,
|
|
218
|
+
localTracker,
|
|
219
|
+
registerBuiltinTrackers,
|
|
220
|
+
TrackerError,
|
|
221
|
+
type AdapterContext,
|
|
222
|
+
type TrackerAdapter,
|
|
223
|
+
type TrackerFetch,
|
|
224
|
+
} from '@smwb/onreel-git-mcp/tracker';
|
|
225
|
+
|
|
226
|
+
// Явный вызов, а не побочный эффект импорта (R-001); повтор — no-op.
|
|
227
|
+
registerBuiltinTrackers();
|
|
228
|
+
console.log(findTracker('github')?.descriptor.title); // 'GitHub'; реестр пуст без этого вызова
|
|
229
|
+
|
|
230
|
+
const fetch: TrackerFetch = (input, init) =>
|
|
231
|
+
Promise.resolve(new Response(JSON.stringify({ number: 7, title: 'Падает сборка', state: 'open', labels: [] }), { status: 200 }));
|
|
232
|
+
|
|
233
|
+
const context: AdapterContext = {
|
|
234
|
+
projectId: 'p1',
|
|
235
|
+
connection: { target: 'o/r', token: 'секрет-только-в-памяти' }, // расшифровку даёт хранилище вызывающего
|
|
236
|
+
fetch,
|
|
237
|
+
store: myLocalIssueStore, // порт LocalIssueStore — нужен только локальному трекеру
|
|
238
|
+
author: 'owner',
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const adapter: TrackerAdapter = (findTracker('github') ?? localTracker()).create(context);
|
|
242
|
+
const issue = await adapter.get('7');
|
|
243
|
+
try {
|
|
244
|
+
await adapter.update('7', { status: 'in_progress' });
|
|
245
|
+
} catch (error) {
|
|
246
|
+
// 'failed' | 'unknown_issue' | 'invalid_request' | 'not_found' | 'unsupported' | 'conflict' | 'rate_limited'
|
|
247
|
+
if (error instanceof TrackerError) console.log(error.failure);
|
|
248
|
+
else throw error;
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Фабрики: `localTracker()`, `sourcecraftTracker()`, `githubTracker()`, `gitlabTracker()`, `jiraTracker()`.
|
|
253
|
+
Сопоставление статусов и приоритетов — открытые таблицы (`SOURCECRAFT_STATUSES`, `JIRA_PRIORITIES`,
|
|
254
|
+
`GITHUB_PRIORITY_LABELS`, `GITLAB_PRIORITY_LABELS`, …), чтобы потребитель видел, во что именно преводится его
|
|
255
|
+
статус. Свои трекеры добавляют в реестр явным `registerTracker(definition)`; повторный `kind` — исключение, а не
|
|
256
|
+
молчаливая замена. Типы порта `LocalIssueStore` (и его строк) реэкспортированы из `…/tracker` — реализацию, как и
|
|
257
|
+
для `WorktreeStore`, пишет потребитель; эталон в памяти — `test/helpers/tracker-fixtures.lib.ts`.
|
|
258
|
+
|
|
259
|
+
Таймаут запроса к трекеру — `TRACKER_TIMEOUT_MS` (15 с), тело ответа ошибки обрезают до
|
|
260
|
+
`TRACKER_ERROR_BODY_MAX` (300 символов) и маскируют; 404 с `missingIsIssue` — `unknown_issue`, а не «неверный
|
|
261
|
+
адрес». 404 с заданным `missing` отдаёт его код и текст (`not_found`), статус из `conflictOn` — `conflict`,
|
|
262
|
+
429 и 403, который выглядит лимитом частоты (`x-ratelimit-remaining: 0` или `retry-after`) — `rate_limited`
|
|
263
|
+
(подробнее — в подразделе ниже).
|
|
264
|
+
|
|
265
|
+
### Фильтры, сортировка и страницы
|
|
266
|
+
|
|
267
|
+
`IssueQuery` — `PageQuery` плюс необязательные фильтры (D-004): `status` (категории статуса), `labels`
|
|
268
|
+
(должны присутствовать все перечисленные), `assignee` (в том же виде, что в `Issue.assignee`; `null` — задачи
|
|
269
|
+
без исполнителя), `author` (логин или slug трекера), `search` (как ищет сам трекер: у SourceCraft — префикс
|
|
270
|
+
заголовка, у локального — подстрока заголовка и описания), `createdAfter`/`createdBefore`/`updatedAfter`/
|
|
271
|
+
`updatedBefore` (мс Unix epoch, границы включительно) и `sort` — `ListSort<IssueSortField>` (`{ field, direction? }`,
|
|
272
|
+
где `direction` — `SortDirection`). Без `sort` — прежний порядок трекера: GitHub, GitLab, Jira и локальный отдают
|
|
273
|
+
новые первыми, SourceCraft — свои (старые первыми). Без `direction` — `desc`.
|
|
274
|
+
`comments` принимает `CommentQuery`: `updatedAfter` (мс) и `order` — `SortDirection` (`asc` по умолчанию).
|
|
275
|
+
Трекеры ограничивают размер страницы (GitHub и GitLab — до 100): `limit` выше потолка вернёт страницу размером
|
|
276
|
+
потолка.
|
|
277
|
+
|
|
278
|
+
Что умеет каждый список, описывают `IssueCapabilities` в разделе `issues` определения трекера:
|
|
279
|
+
`{ list: ListCapabilities<IssueFilter, IssueSortField>, comments: CommentCapabilities }`. `ListCapabilities`
|
|
280
|
+
перечисляет `filters` — фильтры, которые адаптер применяет (любой другой — отказ `unsupported` **до запроса**),
|
|
281
|
+
`clientSide` — часть `filters`, которую адаптер проверяет по полученной странице: такая страница бывает короче
|
|
282
|
+
`limit` и даже пустой при живом `nextCursor`, и `sorts` — поля сортировки. `CommentCapabilities` описывает
|
|
283
|
+
список комментариев так же: `filters` и `clientSide` по `updatedAfter` и `order` вместо `sorts` — допустимые
|
|
284
|
+
порядки, первый из них по умолчанию. Возможности статичны и сериализуются в JSON.
|
|
285
|
+
У определения без раздела читаются `LEGACY_ISSUE_CAPABILITIES` — через `issueCapabilitiesOf(definition)`:
|
|
286
|
+
|
|
287
|
+
| Трекер | `list.filters` | `list.clientSide` | `list.sorts` | `comments` |
|
|
288
|
+
|---|---|---|---|---|
|
|
289
|
+
| GitHub | все `ISSUE_FILTERS` | `status` | `created`, `updated`, `comments` | `updatedAfter`; порядок `asc` |
|
|
290
|
+
| GitLab | все `ISSUE_FILTERS` | `status` | `created`, `updated`, `priority`, `title` | `updatedAfter` (на странице); `asc`, `desc` |
|
|
291
|
+
| Jira | все `ISSUE_FILTERS` | — | `created`, `updated`, `priority`, `title` | как у GitLab |
|
|
292
|
+
| SourceCraft | все `ISSUE_FILTERS` | `assignee` | `created`, `updated`, `priority`, `title` | как у GitLab |
|
|
293
|
+
| локальный | без `author` | без `status` | — | `updatedAfter` (на странице); `asc` |
|
|
294
|
+
|
|
295
|
+
У GitHub фильтры `search` и диапазоны `created*`/`updated*` идут через Search API (30 запросов/мин,
|
|
296
|
+
не больше 1000 результатов). Границы времени там — квалификаторы `q` без кавычек: одна граница —
|
|
297
|
+
`created:>=<ISO>` или `created:<=<ISO>`, обе границы одного поля — один диапазон `created:<ISO>..<ISO>`
|
|
298
|
+
(два отдельных квалификатора GitHub объединяет через OR, и вместо фильтра он отдал бы почти всё, а кавычки
|
|
299
|
+
в дате не читает вовсе — отдал бы пустой список). У SourceCraft статус уходит в QL-фильтр
|
|
300
|
+
(`SOURCECRAFT_STATUS_FILTER`), но всё равно перепроверяется на странице. Правила модели, которые держат тесты:
|
|
301
|
+
|
|
302
|
+
- **R1 — проверка до запроса.** Метод списка сначала зовёт `assertListQuery`/`assertCommentQuery`: фильтр
|
|
303
|
+
или сортировка вне возможностей — `TrackerError('unsupported')` без единого запроса, а неверное значение
|
|
304
|
+
(`limit` < 1, время не целое ≥ 0, пустая строка, метка с `,` там, где трекер ждёт CSV) — `invalid_request`.
|
|
305
|
+
- **R2 — время.** Время в мс Unix epoch, границы включительно; наверх уходят как `isoTime(ms)` (у Jira —
|
|
306
|
+
UTC с округлением минут наружу: `after` вниз, `before` вверх).
|
|
307
|
+
- **R3 — значения.** `labels` — все должны присутствовать; `assignee` — как в `Issue.assignee`, `null` —
|
|
308
|
+
без исполнителя; `author` — логин или slug трекера; `search` — как ищет сам трекер.
|
|
309
|
+
- **R4 — умолчания.** Без `sort` — прежний порядок трекера: GitHub, GitLab, Jira и локальный отдают новые
|
|
310
|
+
первыми, у SourceCraft — свой порядок по умолчанию (старые первыми); без `direction` — `desc`, у комментариев
|
|
311
|
+
без `order` — `asc`.
|
|
312
|
+
- **R5 — страницы.** `clientSide`-фильтр применяется к элементам каждой полученной страницы, `nextCursor` —
|
|
313
|
+
курсор трекера; страницы бывают короче `limit` и пустыми.
|
|
314
|
+
- **R6 — экранирование.** Каждое значение пользователя, попадающее в JQL, QL SourceCraft или `q` поиска GitHub,
|
|
315
|
+
проходит `quoted()` (`\` и `"` экранируются); свободный текст GitHub уходит одной фразой в кавычках, так что
|
|
316
|
+
`repo:чужой/репозиторий` из ввода не добавит чужой репозиторий в выдачу. Даты `isoTime(ms)` в `q` — исключение:
|
|
317
|
+
их кладут без кавычек (значение машинное, `[0-9TZ:.-]`), иначе GitHub не найдёт ни одной задачи.
|
|
318
|
+
- **R7 — прежние трекеры.** Определение без раздела `issues` читается как `LEGACY_ISSUE_CAPABILITIES`
|
|
319
|
+
через `issueCapabilitiesOf()` — свои трекеры потребителя продолжают работать без изменений.
|
|
320
|
+
|
|
321
|
+
Помощники фильтров — публичные символы субпути: `usedFilters` (имена заполненных фильтров в порядке
|
|
322
|
+
констант), `assertListQuery`/`assertCommentQuery` (проверка запроса по возможностям), `issueCapabilitiesOf`,
|
|
323
|
+
`isoTime` (мс → ISO 8601 UTC), `inTimeRange` (включительные границы, заданная граница и неизвестное время —
|
|
324
|
+
не проходит), `containsText` (подстрока без учёта регистра), `quoted` (экранирование для JQL/QL/`q`),
|
|
325
|
+
`keysFor` (обратный поиск по таблице сопоставления). Имена фильтров и полей сортировки — константы
|
|
326
|
+
`ISSUE_FILTERS`, `ISSUE_SORT_FIELDS`, `COMMENT_FILTERS` и выведенные из них типы `IssueFilter`,
|
|
327
|
+
`IssueSortField`, `CommentFilter`. За модель отвечают запись D-004 и правило R-002.
|
|
328
|
+
|
|
329
|
+
**Новые коды отказа.** `TrackerFailure` расширен четырьмя кодами; `requestJson` выставляет их по ответу,
|
|
330
|
+
а потребитель сопоставляет со своим контрактом:
|
|
331
|
+
|
|
332
|
+
| `TrackerFailure` | Когда возникает | Код контракта потребителя |
|
|
333
|
+
|---|---|---|
|
|
334
|
+
| `not_found` | 404 сущности, заданный через `missing` (PR, прогон, задание) | `not_found` |
|
|
335
|
+
| `unsupported` | фильтр/сортировка вне возможностей, неподдержанная операция | `capability_unsupported` |
|
|
336
|
+
| `conflict` | статус из `conflictOn` (405/409/422): переход сейчас невозможен | `lifecycle_conflict` |
|
|
337
|
+
| `rate_limited` | 429, 403 с `x-ratelimit-remaining: 0` и 403 с `retry-after` (вторичные лимиты GitHub: Search API — 30 запросов/мин); задержка — из `retry-after`, иначе `x-ratelimit-reset` (GitHub), иначе `ratelimit-reset` (GitLab) | `rate_limited` |
|
|
338
|
+
|
|
339
|
+
Все четыре кода уже есть в каталоге ошибок контракта (`errorCatalog`), срез контракта не меняется.
|
|
340
|
+
Исчерпывающий `switch` по `TrackerFailure` у потребителя после обновления нужно дополнить.
|
|
341
|
+
|
|
342
|
+
## mcp
|
|
343
|
+
|
|
344
|
+
`serveMcp` — построчный JSON-RPC 2.0 на stdio, только инструменты: `initialize`, `ping`, `tools/list`,
|
|
345
|
+
`tools/call` и `notifications/cancelled`. Своей таблицы команд у библиотеки нет — она приходит параметром,
|
|
346
|
+
инструмент строится из строки таблицы (имя — путь через `_`, свойства — флаги без `--` плюс `request-id`).
|
|
347
|
+
Конверт результата тот же, что у CLI `--json`: `{ok: true, requestId, duplicate, result}` или
|
|
348
|
+
`{ok: false, requestId, error: {code, message, nextSteps}}`.
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
import { mcpToolSpecs, serveMcp, type CommandSpec } from '@smwb/onreel-git-mcp/mcp';
|
|
352
|
+
|
|
353
|
+
const specs: readonly CommandSpec[] = [
|
|
354
|
+
{
|
|
355
|
+
path: ['orch', 'run', 'list'],
|
|
356
|
+
handler: 'verb',
|
|
357
|
+
verb: 'run.list',
|
|
358
|
+
mutation: false,
|
|
359
|
+
flags: [{ name: 'limit', parse: 'int', field: 'limit', help: 'размер страницы' }],
|
|
360
|
+
summary: 'Список Run',
|
|
361
|
+
roles: ['owner'],
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
path: ['orch', 'hello'],
|
|
365
|
+
handler: 'local',
|
|
366
|
+
flags: [{ name: 'name', parse: 'string', required: true, help: 'имя для приветствия' }],
|
|
367
|
+
summary: 'Сказать приветствие',
|
|
368
|
+
roles: ['owner'],
|
|
369
|
+
},
|
|
370
|
+
];
|
|
371
|
+
|
|
372
|
+
const stop = new AbortController();
|
|
373
|
+
process.once('SIGINT', () => stop.abort());
|
|
374
|
+
|
|
375
|
+
await serveMcp({
|
|
376
|
+
specs: mcpToolSpecs(specs), // отсекает строку самой команды `orch mcp`
|
|
377
|
+
execute: async (spec, values, globals) => ({ result: { command: spec.path.join(' '), values }, requestId: globals.requestId }),
|
|
378
|
+
globals: { json: false, help: false },
|
|
379
|
+
io: {
|
|
380
|
+
env: process.env,
|
|
381
|
+
cwd: process.cwd(),
|
|
382
|
+
stdout: (text) => process.stdout.write(text), // канал протокола: больше никаких строк
|
|
383
|
+
stderr: (text) => process.stderr.write(text),
|
|
384
|
+
signal: stop.signal, // SIGINT: начатые вызовы отменяются
|
|
385
|
+
},
|
|
386
|
+
input: process.stdin,
|
|
387
|
+
discovery: (globals, env) => globals.dataDir ?? env['ONREEL_DATA_DIR'],
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Сервер отвечает `serverInfo` с `name: 'onreel'` и версией контракта host; согласовывает любую из
|
|
392
|
+
`MCP_PROTOCOL_VERSIONS` (`2025-11-25` … `2024-11-05`) и отдаёт `structuredContent` от `2025-06-18`. Прочее:
|
|
393
|
+
`notifications/progress` каждые `MCP_PROGRESS_INTERVAL_MS` (15 с), если клиент прислал `progressToken`; строка
|
|
394
|
+
длиннее `MCP_MAX_LINE_BYTES` (8 МиБ) отбрасывается с ошибкой парсинга; после конца stdin начатые вызовы живут
|
|
395
|
+
`MCP_EOF_GRACE_MS` (5 с).
|
|
396
|
+
|
|
397
|
+
**Принципал** (от чьего имени работает сервер) выбирают один раз при запуске: env агента
|
|
398
|
+
(`ONREEL_HOST_URL` + `ONREEL_TOKEN`) — агент; явный `--data-dir` — владелец; иначе владелец по `host.json`,
|
|
399
|
+
каталог для которого даёт `DiscoveryProvider` — библиотека не знает ни имени переменной, ни домашнего каталога CLI
|
|
400
|
+
потребителя. `findAgentAncestor()` поднимается по предкам через `/proc/<pid>/environ` и `/proc/<pid>/status`:
|
|
401
|
+
если сервер запущен внутри процесса агента с урезанным окружением, выбор владельца запрещён
|
|
402
|
+
(`{kind: 'refused'}`), и каждый вызов вернёт `unauthorized` с объяснением. **Это Linux-специфика:** вне Linux
|
|
403
|
+
`/proc` нет, цепочка обрывается, `findAgentAncestor()` возвращает `undefined` (исключения не бросается) и сервер
|
|
404
|
+
при урезанном env выберет владельца. Кому это важно — передаёт `agentAncestor` сам или задаёт `principal` явно.
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
import { findAgentAncestor, mcpPrincipal, principalText, toolOf } from '@smwb/onreel-git-mcp/mcp';
|
|
408
|
+
|
|
409
|
+
const principal = mcpPrincipal({ json: false, help: false }, process.env, findAgentAncestor(), discovery);
|
|
410
|
+
console.log(principalText(principal)); // одна фраза — она попадает в instructions ответа initialize
|
|
411
|
+
console.log(toolOf(specs[0]!, process.env).inputSchema); // обязательны флаги, которые нечем заполнить из env
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## package.json
|
|
415
|
+
|
|
416
|
+
Единственный не-API субпуть — манифест пакета, для инструментов, которым нужны имя и версия
|
|
417
|
+
установленной копии:
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
import manifest from '@smwb/onreel-git-mcp/package.json' with { type: 'json' };
|
|
421
|
+
|
|
422
|
+
console.log(manifest.name, manifest.version);
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Коду вместо этого взять `VERSION` из корневого субпути: он типизирован и не требует
|
|
426
|
+
`resolveJsonModule` и import attributes.
|
|
427
|
+
|
|
428
|
+
## Безопасность
|
|
429
|
+
|
|
430
|
+
- **Хранение токенов.** `writeTrackerToken(dataDir, projectId, token)` кладёт токены в
|
|
431
|
+
`<dataDir>/tracker-secrets.json` с правами **0600** (каталог — 0700), запись идёт через временный файл и
|
|
432
|
+
`rename`, одиночная запись сериализуется очередью, чтобы два подключения не затерли друг друга. `deleteTrackerToken`
|
|
433
|
+
и пустая строка убирают значение. Наружу из хранилища выходит только признак «токен есть» (`hasTrackerToken`),
|
|
434
|
+
а при чтении и записи значение регистрируется в приёмнике секретов.
|
|
435
|
+
- **Маскирование секретов.** `redactSecretText` и `SecretSink` (по умолчанию `defaultSecretSink`) — один redactor
|
|
436
|
+
на библиотеку: его применяют `GitError` (D-272), `requestJson` (тексты отказов трекера) и внутренний отказ
|
|
437
|
+
`serveMcp`. Свой журнал потребителя подключают приёмником `SecretSink`; известные значения добавляют до
|
|
438
|
+
форматирования текста, короткая строка (менее 8 символов) не регистрируется — она дала бы слишком много ложных
|
|
439
|
+
срабатываний.
|
|
440
|
+
- **Ограничение путей.** `checkRepoPath` отказывает базовому репозиторию, который абсолютным путём или по realpath
|
|
441
|
+
лежит внутри каталога данных (там ledger и токены), `prepareWorktreeParent` создаёт каталоги по одному и не
|
|
442
|
+
принимает симлинк, `checkManagedPath` перед удалением проверяет и сам каталог, и его realpath. `listWorktreeDir`
|
|
443
|
+
и `readWorktreeFile` работают только внутри realpath worktree, `.git` не показываются, симлинк за пределы —
|
|
444
|
+
`PathEscapeError` / `DirRefusal('unsafe_path')`.
|
|
445
|
+
- **Канал stdout.** В `serveMcp` `io.stdout` занят протоколом: вывод обработчика команды уходит в никуда, а
|
|
446
|
+
сообщения трекеров и git в журнал идут через `io.stderr` уже замаскированными.
|
|
447
|
+
- **Окружение детей.** `runGit` снимает унаследованные `GIT_*` (иначе `GIT_DIR` увёл бы host работать с чужим
|
|
448
|
+
репозиторием), ставит `GIT_TERMINAL_PROMPT=0`, `GIT_OPTIONAL_LOCKS=0` и `LC_ALL=C`, права создаваемых файлов
|
|
449
|
+
берёт из маски сессии (`ONREEL_SESSION_UMASK`).
|
|
450
|
+
|
|
451
|
+
## Совместимость
|
|
452
|
+
|
|
453
|
+
- Только ESM (`"type": "module"`): CJS-потребителю нужен `import()` или обёртка.
|
|
454
|
+
- Node.js >= 24, Linux; macOS/Windows не тестируются (см. `/proc` и групповой `SIGKILL` выше).
|
|
455
|
+
- Публикуется собранный ESM и `.d.ts` из `dist/` (D-002) — суффиксы `.ts` в импортах исходников стирает сборка,
|
|
456
|
+
их не нужно править в потребителе.
|
|
457
|
+
- `sideEffects: false`, один рантайм-зависимый пакет — `zod`.
|
|
458
|
+
- Реестр трекеров, пул git, замки worktree и redactor — модульная глобальна на процесс: несколько независимых
|
|
459
|
+
«хостов» в одном процессе делят их. Запросы к внешним API идут `fetch` из контекста; узлы без TLS-цепей
|
|
460
|
+
внешнего трекера — задача вызывающего.
|
|
461
|
+
- Имена `ONREEL_*` и `HOST_CONTRACT_VERSION` (`1.14`) заморожены: они держат interop с onreel.
|
|
462
|
+
|
|
463
|
+
## Как внести изменение
|
|
464
|
+
|
|
465
|
+
1. Начните с задачи в трекере репозитория и переведите её в «в работе» (правила — в `AGENTS.md`).
|
|
466
|
+
2. Реализуйте изменение; новый публичный символ или субпуть описывают здесь, в README, и добавляют
|
|
467
|
+
компилируемый пример в [`examples/`](examples).
|
|
468
|
+
3. Прогоните проверки так же, как CI:
|
|
469
|
+
|
|
470
|
+
```sh
|
|
471
|
+
npm run typecheck # src, test и examples
|
|
472
|
+
npm run lint
|
|
473
|
+
npm test
|
|
474
|
+
npm run build
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
4. Изменение публичной поверхности (субпутевые экспорты, имена и сигнатуры публичных символов) требует changeset:
|
|
478
|
+
`npx changeset`, выбрать этот пакет и `minor` для обратно-совместимого дополнения. Без changeset релизный
|
|
479
|
+
workflow не выпустит версию.
|
|
480
|
+
5. Откройте PR с заголовком и коммитами, начинающимися с номера задачи (`#12 …`); в `main` — только через PR
|
|
481
|
+
с зелёным CI. После мержа push в `main` запускает релизный workflow: pending-ченджсеты собираются в
|
|
482
|
+
«Version Packages» PR, а когда их нет — `changeset publish` публикует пакет и ставит теги.
|
|
483
|
+
|
|
484
|
+
## Происхождение
|
|
485
|
+
|
|
486
|
+
Код перенесён из onreel @ `b73407be076a5df73931d1340f70910ca13c908d` (`src/host/worktree/`,
|
|
487
|
+
`src/host/tracker/`, `src/cli/mcp/`, нужная часть `src/contract/`) с заменой внутренних связок на швы ядра.
|
|
488
|
+
Происхождение по файлам — в шапках исходников. Контроллеры `@smwb/srv`, CLI-обвязка и реализации хранилищ поверх
|
|
489
|
+
ledger остаются в onreel: это клей хоста, а не код библиотеки.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,4 @@ export * from './contract/contract.const.ts';
|
|
|
2
2
|
export * from './contract/contract.schema.ts';
|
|
3
3
|
export * from './contract/contract.types.ts';
|
|
4
4
|
export * from './contract/contract.lib.ts';
|
|
5
|
-
|
|
6
|
-
export declare const VERSION = "0.1.0";
|
|
5
|
+
export declare const VERSION: string;
|
package/dist/index.js
CHANGED
|
@@ -56,8 +56,98 @@ import {
|
|
|
56
56
|
worktreeStatusSchema
|
|
57
57
|
} from "./chunk-4ZGRXIVA.js";
|
|
58
58
|
|
|
59
|
+
// package.json
|
|
60
|
+
var package_default = {
|
|
61
|
+
name: "@smwb/onreel-git-mcp",
|
|
62
|
+
version: "0.3.0",
|
|
63
|
+
description: "\u0411\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0430 \u043C\u043E\u0434\u0443\u043B\u0435\u0439, \u0432\u044B\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0445 \u0438\u0437 onreel: git-\u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0438 \u0438 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0435 \u043F\u0443\u0442\u0438, \u0441\u043F\u0438\u0441\u043E\u043A \u0444\u0430\u0439\u043B\u043E\u0432 worktree, \u0436\u0438\u0437\u043D\u0435\u043D\u043D\u044B\u0439 \u0446\u0438\u043A\u043B worktree \u043F\u0440\u043E\u0442\u0438\u0432 \u043F\u043E\u0440\u0442\u0430 WorktreeStore, \u0430\u0434\u0430\u043F\u0442\u0435\u0440\u044B \u0442\u0440\u0435\u043A\u0435\u0440\u043E\u0432 GitHub/GitLab/Jira/SourceCraft/\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0438 MCP-\u0441\u0435\u0440\u0432\u0435\u0440 \u043D\u0430 stdio",
|
|
64
|
+
keywords: [
|
|
65
|
+
"git",
|
|
66
|
+
"worktree",
|
|
67
|
+
"mcp",
|
|
68
|
+
"issue-tracker",
|
|
69
|
+
"github",
|
|
70
|
+
"gitlab",
|
|
71
|
+
"jira",
|
|
72
|
+
"sourcecraft",
|
|
73
|
+
"onreel",
|
|
74
|
+
"typescript"
|
|
75
|
+
],
|
|
76
|
+
type: "module",
|
|
77
|
+
engines: {
|
|
78
|
+
node: ">=24"
|
|
79
|
+
},
|
|
80
|
+
files: [
|
|
81
|
+
"dist"
|
|
82
|
+
],
|
|
83
|
+
sideEffects: false,
|
|
84
|
+
publishConfig: {
|
|
85
|
+
access: "public",
|
|
86
|
+
registry: "https://registry.npmjs.org/"
|
|
87
|
+
},
|
|
88
|
+
repository: {
|
|
89
|
+
type: "git",
|
|
90
|
+
url: "git+https://git.sourcecraft.dev/elfenmail/onreel-git-mcp.git"
|
|
91
|
+
},
|
|
92
|
+
homepage: "https://git.sourcecraft.dev/elfenmail/onreel-git-mcp",
|
|
93
|
+
license: "MIT",
|
|
94
|
+
exports: {
|
|
95
|
+
".": {
|
|
96
|
+
types: "./dist/index.d.ts",
|
|
97
|
+
import: "./dist/index.js",
|
|
98
|
+
default: "./dist/index.js"
|
|
99
|
+
},
|
|
100
|
+
"./git": {
|
|
101
|
+
types: "./dist/git/index.d.ts",
|
|
102
|
+
import: "./dist/git/index.js",
|
|
103
|
+
default: "./dist/git/index.js"
|
|
104
|
+
},
|
|
105
|
+
"./files": {
|
|
106
|
+
types: "./dist/files/index.d.ts",
|
|
107
|
+
import: "./dist/files/index.js",
|
|
108
|
+
default: "./dist/files/index.js"
|
|
109
|
+
},
|
|
110
|
+
"./worktree": {
|
|
111
|
+
types: "./dist/worktree/index.d.ts",
|
|
112
|
+
import: "./dist/worktree/index.js",
|
|
113
|
+
default: "./dist/worktree/index.js"
|
|
114
|
+
},
|
|
115
|
+
"./tracker": {
|
|
116
|
+
types: "./dist/tracker/index.d.ts",
|
|
117
|
+
import: "./dist/tracker/index.js",
|
|
118
|
+
default: "./dist/tracker/index.js"
|
|
119
|
+
},
|
|
120
|
+
"./mcp": {
|
|
121
|
+
types: "./dist/mcp/index.d.ts",
|
|
122
|
+
import: "./dist/mcp/index.js",
|
|
123
|
+
default: "./dist/mcp/index.js"
|
|
124
|
+
},
|
|
125
|
+
"./package.json": "./package.json"
|
|
126
|
+
},
|
|
127
|
+
scripts: {
|
|
128
|
+
build: "tsup && tsc -p tsconfig.build.json",
|
|
129
|
+
typecheck: "tsc -p tsconfig.json && tsc -p tsconfig.examples.json",
|
|
130
|
+
lint: "oxlint --type-aware",
|
|
131
|
+
test: 'node --test "test/**/*.test.ts"',
|
|
132
|
+
changeset: "changeset",
|
|
133
|
+
version: "changeset version",
|
|
134
|
+
release: "npm run build && changeset publish"
|
|
135
|
+
},
|
|
136
|
+
dependencies: {
|
|
137
|
+
zod: "^4.6.5"
|
|
138
|
+
},
|
|
139
|
+
devDependencies: {
|
|
140
|
+
"@changesets/cli": "2.29.2",
|
|
141
|
+
"@types/node": "24.19.1",
|
|
142
|
+
oxlint: "1.87.0",
|
|
143
|
+
"oxlint-tsgolint": "7.0.2003",
|
|
144
|
+
tsup: "8.5.1",
|
|
145
|
+
typescript: "7.0.2"
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
59
149
|
// src/index.ts
|
|
60
|
-
var VERSION =
|
|
150
|
+
var VERSION = package_default.version;
|
|
61
151
|
export {
|
|
62
152
|
CONTRACT_ERROR_MESSAGE_MAX,
|
|
63
153
|
CONTRACT_ERROR_NEXT_STEP_MAX,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Корневой подпуть пакета @smwb/onreel-git-mcp: срез контракта (константы, схемы, типы, чистые\n// функции) и версия пакета. Всё без побочных эффектов при импорте (R-001). Швы ядра\n// (SecretSink, WorktreeStore, LocalIssueStore) и модули git/files/worktree/tracker/mcp\n// экспортируют свои подпути — см. exports в package.json и README.\nexport * from './contract/contract.const.ts';\nexport * from './contract/contract.schema.ts';\nexport * from './contract/contract.types.ts';\nexport * from './contract/contract.lib.ts';\n\n
|
|
1
|
+
{"version":3,"sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@smwb/onreel-git-mcp\",\n \"version\": \"0.3.0\",\n \"description\": \"Библиотека модулей, выделенных из onreel: git-операции и безопасные пути, список файлов worktree, жизненный цикл worktree против порта WorktreeStore, адаптеры трекеров GitHub/GitLab/Jira/SourceCraft/локального и MCP-сервер на stdio\",\n \"keywords\": [\n \"git\",\n \"worktree\",\n \"mcp\",\n \"issue-tracker\",\n \"github\",\n \"gitlab\",\n \"jira\",\n \"sourcecraft\",\n \"onreel\",\n \"typescript\"\n ],\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=24\"\n },\n \"files\": [\n \"dist\"\n ],\n \"sideEffects\": false,\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://git.sourcecraft.dev/elfenmail/onreel-git-mcp.git\"\n },\n \"homepage\": \"https://git.sourcecraft.dev/elfenmail/onreel-git-mcp\",\n \"license\": \"MIT\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"default\": \"./dist/index.js\"\n },\n \"./git\": {\n \"types\": \"./dist/git/index.d.ts\",\n \"import\": \"./dist/git/index.js\",\n \"default\": \"./dist/git/index.js\"\n },\n \"./files\": {\n \"types\": \"./dist/files/index.d.ts\",\n \"import\": \"./dist/files/index.js\",\n \"default\": \"./dist/files/index.js\"\n },\n \"./worktree\": {\n \"types\": \"./dist/worktree/index.d.ts\",\n \"import\": \"./dist/worktree/index.js\",\n \"default\": \"./dist/worktree/index.js\"\n },\n \"./tracker\": {\n \"types\": \"./dist/tracker/index.d.ts\",\n \"import\": \"./dist/tracker/index.js\",\n \"default\": \"./dist/tracker/index.js\"\n },\n \"./mcp\": {\n \"types\": \"./dist/mcp/index.d.ts\",\n \"import\": \"./dist/mcp/index.js\",\n \"default\": \"./dist/mcp/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"scripts\": {\n \"build\": \"tsup && tsc -p tsconfig.build.json\",\n \"typecheck\": \"tsc -p tsconfig.json && tsc -p tsconfig.examples.json\",\n \"lint\": \"oxlint --type-aware\",\n \"test\": \"node --test \\\"test/**/*.test.ts\\\"\",\n \"changeset\": \"changeset\",\n \"version\": \"changeset version\",\n \"release\": \"npm run build && changeset publish\"\n },\n \"dependencies\": {\n \"zod\": \"^4.6.5\"\n },\n \"devDependencies\": {\n \"@changesets/cli\": \"2.29.2\",\n \"@types/node\": \"24.19.1\",\n \"oxlint\": \"1.87.0\",\n \"oxlint-tsgolint\": \"7.0.2003\",\n \"tsup\": \"8.5.1\",\n \"typescript\": \"7.0.2\"\n }\n}\n","// Корневой подпуть пакета @smwb/onreel-git-mcp: срез контракта (константы, схемы, типы, чистые\n// функции) и версия пакета. Всё без побочных эффектов при импорте (R-001). Швы ядра\n// (SecretSink, WorktreeStore, LocalIssueStore) и модули git/files/worktree/tracker/mcp\n// экспортируют свои подпути — см. exports в package.json и README.\nimport pkg from '../package.json' with { type: 'json' };\n\nexport * from './contract/contract.const.ts';\nexport * from './contract/contract.schema.ts';\nexport * from './contract/contract.types.ts';\nexport * from './contract/contract.lib.ts';\n\n// Версия пакета — из version в package.json: статический импорт JSON, его значение tsup подставляет\n// в бандл на сборке, поэтому VERSION не расходится с релизом changesets (тот правит только version).\nexport const VERSION: string = pkg.version;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,aAAe;AAAA,EACf,UAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,MAAQ;AAAA,EACR,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AAAA,EACA,OAAS;AAAA,IACP;AAAA,EACF;AAAA,EACA,aAAe;AAAA,EACf,eAAiB;AAAA,IACf,QAAU;AAAA,IACV,UAAY;AAAA,EACd;AAAA,EACA,YAAc;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,EACT;AAAA,EACA,UAAY;AAAA,EACZ,SAAW;AAAA,EACX,SAAW;AAAA,IACT,KAAK;AAAA,MACH,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,SAAS;AAAA,MACP,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,WAAW;AAAA,MACT,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,cAAc;AAAA,MACZ,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,aAAa;AAAA,MACX,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,SAAS;AAAA,MACP,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,kBAAkB;AAAA,EACpB;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,WAAa;AAAA,IACb,MAAQ;AAAA,IACR,MAAQ;AAAA,IACR,WAAa;AAAA,IACb,SAAW;AAAA,IACX,SAAW;AAAA,EACb;AAAA,EACA,cAAgB;AAAA,IACd,KAAO;AAAA,EACT;AAAA,EACA,iBAAmB;AAAA,IACjB,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,QAAU;AAAA,IACV,mBAAmB;AAAA,IACnB,MAAQ;AAAA,IACR,YAAc;AAAA,EAChB;AACF;;;AC1EO,IAAM,UAAkB,gBAAI;","names":[]}
|