engineering-memory 1.11.11 → 1.11.13
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/dispatcher/sections.mjs +2 -2
- package/package.json +1 -1
- package/runtime/build.json +1 -1
- package/runtime/dist/src/mcp/onboarding-tools.js +833 -154
- package/runtime/dist/src/mcp/questionnaire-tools.js +55 -16
- package/runtime/dist/src/mcp/tool-definitions.js +107 -24
- package/runtime/dist/src/mcp/worktree-tools.js +349 -91
- package/runtime/dist/src/runtime/bridge-service.js +127 -19
- package/runtime/dist/src/runtime/create-bridge-service.js +2 -0
- package/runtime/dist/src/runtime/language-store.js +25 -0
- package/runtime/dist/src/runtime/offline-outbox.js +4 -4
- package/runtime/dist/src/runtime/privacy-detector.js +14 -5
- package/runtime/dist/src/runtime/questionnaire-store.js +26 -7
- package/runtime/dist/src/runtime/task-start.js +115 -33
- package/skill/references/questionnaires.md +8 -3
|
@@ -5,6 +5,7 @@ import { configuredBase, sourceLabel, taskStartChoice, taskStartDefinition, } fr
|
|
|
5
5
|
import { sha256, stableStringify } from '../utilities/hash.js';
|
|
6
6
|
import { unreadableWorktree } from '../runtime/worktree-pool.js';
|
|
7
7
|
import { askQuestionnaire } from './questionnaire-tools.js';
|
|
8
|
+
import { languageTag } from '../runtime/questionnaire-store.js';
|
|
8
9
|
const repo = z.string().min(1);
|
|
9
10
|
const branch = z.string().min(1).max(255);
|
|
10
11
|
const remote = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/);
|
|
@@ -49,6 +50,170 @@ const policy = {
|
|
|
49
50
|
heartbeatSeconds: z.number().int().min(5).max(300),
|
|
50
51
|
inactivitySeconds: z.number().int().min(60).max(86400),
|
|
51
52
|
};
|
|
53
|
+
const preferenceCopy = {
|
|
54
|
+
en: {
|
|
55
|
+
message: 'Save the shared branch preferences of this project? Nothing is saved until you answer.',
|
|
56
|
+
context: 'These branches belong to the whole project: every member who starts a task here is offered them as the branch the task starts from. Only a project or organization administrator can change them. No branch is created, moved or deleted; only the preference is recorded.',
|
|
57
|
+
example: 'Once the development role points at origin/develop, the start form of the next task offers origin/develop as its first choice.',
|
|
58
|
+
roles: { development: 'development', production: 'production', test: 'test' },
|
|
59
|
+
ask: (role, source) => source
|
|
60
|
+
? `Save ${source} as the shared ${role} branch?`
|
|
61
|
+
: `Record that this project has no ${role} branch?`,
|
|
62
|
+
save: 'Save this choice',
|
|
63
|
+
saved: 'This branch is recorded as the project default now, for every member.',
|
|
64
|
+
leave: 'Leave unchanged',
|
|
65
|
+
unchanged: 'Nothing changes. This role stays as it is and can be answered another time.',
|
|
66
|
+
},
|
|
67
|
+
tr: {
|
|
68
|
+
message: 'Projenin ortak dal tercihleri kaydedilsin mi? Sen cevaplayana kadar hiçbir şey kaydedilmez.',
|
|
69
|
+
context: 'Bu dallar projenin tamamı için geçerli: burada görev başlatan herkese, görevin başlayacağı dal olarak bunlar önerilir. Yalnızca proje ya da organizasyon yöneticisi değiştirebilir. Hiçbir dal oluşturulmaz, taşınmaz veya silinmez; yalnızca tercih kaydedilir.',
|
|
70
|
+
example: 'Geliştirme dalı origin/develop olarak kaydedilirse, bir sonraki görevin başlangıç formunda ilk seçenek origin/develop olur.',
|
|
71
|
+
roles: { development: 'Geliştirme', production: 'Canlı', test: 'Test' },
|
|
72
|
+
ask: (role, source) => source
|
|
73
|
+
? `${role} dalı ${source} olarak kaydedilsin mi?`
|
|
74
|
+
: `Bu projede ${role.toLocaleLowerCase('tr')} dalı olmadığı kaydedilsin mi?`,
|
|
75
|
+
save: 'Bu tercihi kaydet',
|
|
76
|
+
saved: 'Bu dal şimdi proje varsayılanı olarak kaydedilir; ekipteki herkes onu görür.',
|
|
77
|
+
leave: 'Şimdilik değiştirme',
|
|
78
|
+
unchanged: 'Hiçbir şey değişmez. Bu rol olduğu gibi kalır, sonra yeniden sorulabilir.',
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
const fallbackCopy = {
|
|
82
|
+
en: {
|
|
83
|
+
message: (remote, local, inPlace) => `The remote ${remote} could not be fetched. Should the task start from local ${local} ${inPlace ? 'in this folder' : 'in a separate worktree'}?`,
|
|
84
|
+
context: 'The branch you chose cannot be reached, so its exact commit is unavailable. The local copy is whatever was fetched last: commits pushed to the remote since then are not in it. Nothing is created until you answer.',
|
|
85
|
+
example: (remote, local) => `Continuing creates the new branch at ${local}; ${remote} can be merged into it once the network is back.`,
|
|
86
|
+
approve: 'Continue',
|
|
87
|
+
approved: (local) => `The new branch is created at ${local} now and the task starts.`,
|
|
88
|
+
defer: 'Not now',
|
|
89
|
+
deferred: 'Nothing is created or reserved. The task waits, and it can be started again once the remote answers.',
|
|
90
|
+
},
|
|
91
|
+
tr: {
|
|
92
|
+
message: (remote, local, inPlace) => `${remote} uzak dalına ulaşılamadı. Görev, yereldeki ${local} üzerinden ${inPlace ? 'bu klasörde' : 'ayrı bir worktree’de'} başlasın mı?`,
|
|
93
|
+
context: 'Seçtiğin dala ulaşılamadığı için tam commit’i alınamıyor. Yerel kopya, en son çekilen hâli gösterir: o günden sonra uzak dala giden commit’ler burada yok. Sen cevaplayana kadar hiçbir şey oluşturulmaz.',
|
|
94
|
+
example: (remote, local) => `Devam edersen yeni dal ${local} üzerinden açılır; ağ düzeldiğinde ${remote} dalını bu dala birleştirebilirsin.`,
|
|
95
|
+
approve: 'Devam et',
|
|
96
|
+
approved: (local) => `Yeni dal şimdi ${local} üzerinden açılır ve görev başlar.`,
|
|
97
|
+
defer: 'Şimdilik başlatma',
|
|
98
|
+
deferred: 'Hiçbir şey oluşturulmaz, hiçbir yer ayrılmaz. Görev bekler; uzak dala ulaşıldığında yeniden başlatılabilir.',
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
const forgetCopy = {
|
|
102
|
+
en: {
|
|
103
|
+
message: (task, folder) => `The ${folder} folder of task ${task} can no longer be read as a Git checkout. Should Engineering Memory stop tracking it?`,
|
|
104
|
+
context: (pendingDelivery) => 'A checkout that cannot be read is still counted against the worktree limit although nothing can use it; forgetting it frees that slot. No file in the folder is touched or deleted, and the branch stays.' +
|
|
105
|
+
(pendingDelivery
|
|
106
|
+
? ' That task closed without its delivery being settled, so any undelivered work exists only in that folder.'
|
|
107
|
+
: ''),
|
|
108
|
+
example: (task, folder) => `After this, ${task} no longer appears in the worktree list and the ${folder} folder stays on disk exactly as it is.`,
|
|
109
|
+
forget: 'Stop tracking it; leave the files alone',
|
|
110
|
+
forgotten: 'The record is removed and the slot is free again. The folder, its files and its branch stay untouched.',
|
|
111
|
+
keep: 'Keep tracking it',
|
|
112
|
+
kept: 'Nothing changes. The record stays and keeps counting against the limit.',
|
|
113
|
+
},
|
|
114
|
+
tr: {
|
|
115
|
+
message: (task, folder) => `${task} görevinin ${folder} klasörü artık bir Git çalışma kopyası olarak okunamıyor. Engineering Memory bu klasörü takip etmeyi bıraksın mı?`,
|
|
116
|
+
context: (pendingDelivery) => 'Okunamayan bir çalışma kopyası hiçbir işe yaramadığı hâlde worktree sınırına sayılmaya devam eder; takipten çıkarılırsa o yer boşalır. Klasördeki hiçbir dosyaya dokunulmaz, hiçbiri silinmez; dal da kalır.' +
|
|
117
|
+
(pendingDelivery
|
|
118
|
+
? ' Bu görev teslimi sonuçlanmadan kapandı; teslim edilmemiş iş varsa yalnızca bu klasörde duruyor.'
|
|
119
|
+
: ''),
|
|
120
|
+
example: (task, folder) => `Bundan sonra ${task} worktree listesinde görünmez, ${folder} klasörü ise diskte olduğu gibi kalır.`,
|
|
121
|
+
forget: 'Takip etmeyi bırak; dosyalara dokunma',
|
|
122
|
+
forgotten: 'Kayıt silinir ve sınırdaki yer boşalır. Klasör, içindeki dosyalar ve dal olduğu gibi kalır.',
|
|
123
|
+
keep: 'Takip etmeye devam et',
|
|
124
|
+
kept: 'Hiçbir şey değişmez. Kayıt durur ve sınıra sayılmaya devam eder.',
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
const retireCopy = {
|
|
128
|
+
en: {
|
|
129
|
+
missing: (folder) => `The ${folder} folder is gone but its worktree record is still here. Remove the record?`,
|
|
130
|
+
message: (folder, branch) => `${folder} is a worktree left in the old location (branch ${branch}). Remove the folder?`,
|
|
131
|
+
context: (missing) => 'Folders in the old location are no longer part of the pool: they are never reused and do not count toward the limit.' +
|
|
132
|
+
(missing
|
|
133
|
+
? ' Only the record is left, so there is no folder to remove and no file is read or deleted.'
|
|
134
|
+
: ' The branch and its commits stay. Git removes only a clean folder, and the files it ignores go with it.'),
|
|
135
|
+
example: (folder, branch, missing) => missing
|
|
136
|
+
? `After this, ${folder} is gone from the worktree list and nothing on disk changes.`
|
|
137
|
+
: `After this, ${folder} is gone from the worktree list and branch ${branch} still holds every commit made on it.`,
|
|
138
|
+
retire: 'Remove the folder',
|
|
139
|
+
retired: (missing) => missing
|
|
140
|
+
? 'The stale record is removed. Nothing on disk changes.'
|
|
141
|
+
: 'Git removes the folder and Engineering Memory drops its record. The branch and its commits stay.',
|
|
142
|
+
keep: 'Keep it',
|
|
143
|
+
kept: 'Nothing changes. The folder and its record stay exactly as they are.',
|
|
144
|
+
},
|
|
145
|
+
tr: {
|
|
146
|
+
missing: (folder) => `${folder} klasörü artık yok ama worktree kaydı duruyor. Kayıt silinsin mi?`,
|
|
147
|
+
message: (folder, branch) => `${folder}, eski konumda kalmış bir worktree (dal ${branch}). Klasör kaldırılsın mı?`,
|
|
148
|
+
context: (missing) => 'Eski konumdaki klasörler artık havuzun parçası değil: yeniden kullanılmıyorlar ve sınıra da sayılmıyorlar.' +
|
|
149
|
+
(missing
|
|
150
|
+
? ' Geriye yalnızca kayıt kaldı; kaldırılacak bir klasör yok, hiçbir dosya okunmaz veya silinmez.'
|
|
151
|
+
: ' Dal ve üzerindeki commit’ler kalır. Git yalnızca temiz bir klasörü kaldırır; kaldırırken .gitignore kapsamındaki dosyalar da gider.'),
|
|
152
|
+
example: (folder, branch, missing) => missing
|
|
153
|
+
? `Bundan sonra ${folder} worktree listesinde görünmez; diskte hiçbir şey değişmez.`
|
|
154
|
+
: `Bundan sonra ${folder} worktree listesinde görünmez; ${branch} dalındaki commit’ler ise yerinde durur.`,
|
|
155
|
+
retire: 'Klasörü kaldır',
|
|
156
|
+
retired: (missing) => missing
|
|
157
|
+
? 'Boşta kalan kayıt silinir. Diskte hiçbir şey değişmez.'
|
|
158
|
+
: 'Git klasörü kaldırır, Engineering Memory de kaydını siler. Dal ve commit’ler kalır.',
|
|
159
|
+
keep: 'Kalsın',
|
|
160
|
+
kept: 'Hiçbir şey değişmez. Klasör de kaydı da olduğu gibi kalır.',
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
const unownedCopy = {
|
|
164
|
+
en: {
|
|
165
|
+
message: (folder) => `Nobody is recorded as using the ${folder} folder. If the agent that worked there has stopped and its delivery is finished, should the clean checkout become reusable?`,
|
|
166
|
+
context: 'This checkout was found without an ownership record, so it is protected and handed to no task. Releasing it runs the safety checks again first: a folder with uncommitted work is refused. No file and no branch is ever deleted.',
|
|
167
|
+
example: (commit) => `Once it is released, the next task can start in that folder, and the work committed at ${commit} stays on its branch.`,
|
|
168
|
+
release: 'Prior work finished; release after safety checks',
|
|
169
|
+
released: 'The safety checks run and, if they pass, the folder returns to the pool.',
|
|
170
|
+
preserve: 'Keep protected',
|
|
171
|
+
preserved: 'Nothing changes. The folder stays protected and is given to no task.',
|
|
172
|
+
},
|
|
173
|
+
tr: {
|
|
174
|
+
message: (folder) => `${folder} klasörünü kimin kullandığı kayıtlı değil. Burada çalışan ajan durduysa ve teslimi bittiyse, temiz klasör yeniden kullanılabilir hâle gelsin mi?`,
|
|
175
|
+
context: 'Bu çalışma kopyası sahiplik kaydı olmadan bulundu; bu yüzden korunuyor ve hiçbir göreve verilmiyor. Serbest bırakmadan önce güvenlik kontrolleri yeniden çalışır: kaydedilmemiş iş varsa işlem yapılmaz. Hiçbir dosya ve hiçbir dal silinmez.',
|
|
176
|
+
example: (commit) => `Serbest bırakılırsa sıradaki görev bu klasörde başlayabilir; ${commit} commit’ine kaydedilmiş iş dalında durmaya devam eder.`,
|
|
177
|
+
release: 'Önceki iş bitti; güvenlik kontrollerinden sonra kullanılabilir yap',
|
|
178
|
+
released: 'Güvenlik kontrolleri çalışır; geçerse klasör havuza döner.',
|
|
179
|
+
preserve: 'Korumaya devam et',
|
|
180
|
+
preserved: 'Hiçbir şey değişmez. Klasör korunur ve hiçbir göreve verilmez.',
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
const cancelCopy = {
|
|
184
|
+
en: {
|
|
185
|
+
message: (task) => `${task} will not be delivered. What should happen to this folder?`,
|
|
186
|
+
context: (clean, managed) => 'A cancelled delivery leaves the folder reserved for this task until you say what happens to it, and a reserved folder keeps counting against the worktree limit. The branch and every commit on it stay either way.' +
|
|
187
|
+
(clean
|
|
188
|
+
? ''
|
|
189
|
+
: managed
|
|
190
|
+
? ' The folder has uncommitted changes; saving writes them to a Git ref they can be restored from.'
|
|
191
|
+
: ' The folder has uncommitted changes; this is your own folder, so its files are never touched when it is freed.'),
|
|
192
|
+
example: 'Freeing the folder lets the next task start in it; keeping it leaves it reserved.',
|
|
193
|
+
release: 'Free the folder; the branch stays',
|
|
194
|
+
released: 'The folder returns to the pool. Its files are left alone and the branch stays.',
|
|
195
|
+
save: 'Save the changes and free the folder',
|
|
196
|
+
saved: 'The uncommitted changes are written to a Git ref first, then the folder is freed. They can be restored from that ref.',
|
|
197
|
+
keep: 'Keep the folder as it is',
|
|
198
|
+
kept: 'Nothing changes. The folder stays reserved for this task.',
|
|
199
|
+
},
|
|
200
|
+
tr: {
|
|
201
|
+
message: (task) => `${task} teslim edilmeyecek. Bu klasör ne olsun?`,
|
|
202
|
+
context: (clean, managed) => 'Teslimi iptal edilen bir klasör, sen ne olacağını söyleyene kadar bu göreve ayrılmış kalır ve ayrılmış klasör worktree sınırına sayılmaya devam eder. Dal ve üzerindeki commit’ler her hâlükârda durur.' +
|
|
203
|
+
(clean
|
|
204
|
+
? ''
|
|
205
|
+
: managed
|
|
206
|
+
? ' Klasörde kaydedilmemiş değişiklikler var; kaydedilirse bir Git ref’ine yazılır ve oradan geri alınabilir.'
|
|
207
|
+
: ' Klasörde kaydedilmemiş değişiklikler var; burası senin kendi klasörün olduğu için boşaltılsa da dosyalara dokunulmaz.'),
|
|
208
|
+
example: 'Klasör boşaltılırsa sıradaki görev burada başlayabilir; kalsın dersen bu göreve ayrılmış hâlde durur.',
|
|
209
|
+
release: 'Klasörü boşalt; branch kalsın',
|
|
210
|
+
released: 'Klasör havuza döner. Dosyalarına dokunulmaz, dal kalır.',
|
|
211
|
+
save: 'Değişiklikleri kaydedip klasörü boşalt',
|
|
212
|
+
saved: 'Kaydedilmemiş değişiklikler önce bir Git ref’ine yazılır, sonra klasör boşaltılır. Değişiklikler o ref’ten geri alınabilir.',
|
|
213
|
+
keep: 'Klasör olduğu gibi kalsın',
|
|
214
|
+
kept: 'Hiçbir şey değişmez. Klasör bu göreve ayrılmış hâlde kalır.',
|
|
215
|
+
},
|
|
216
|
+
};
|
|
52
217
|
const baseSchema = z.discriminatedUnion('kind', [
|
|
53
218
|
z.strictObject({
|
|
54
219
|
kind: z.literal('current'),
|
|
@@ -79,10 +244,11 @@ export function registerWorktreeTools(server, service) {
|
|
|
79
244
|
development: preference.optional(),
|
|
80
245
|
production: preference.optional(),
|
|
81
246
|
test: preference.optional(),
|
|
82
|
-
language:
|
|
247
|
+
language: languageTag.optional(),
|
|
83
248
|
presentation,
|
|
84
249
|
}),
|
|
85
250
|
}, async (input, context) => {
|
|
251
|
+
const language = (await service.language(input.language))?.startsWith('tr') ? 'tr' : 'en';
|
|
86
252
|
const current = await service.projectGitPreferences(input);
|
|
87
253
|
if (!current.ok)
|
|
88
254
|
return output(current);
|
|
@@ -100,18 +266,9 @@ export function registerWorktreeTools(server, service) {
|
|
|
100
266
|
const roles = ['development', 'production', 'test'].filter((role) => Boolean(input[role]));
|
|
101
267
|
if (roles.length === 0)
|
|
102
268
|
return output({ ok: true, data: { unchanged: true } });
|
|
103
|
-
const
|
|
269
|
+
const source = (role, none) => {
|
|
104
270
|
const value = input[role];
|
|
105
|
-
return
|
|
106
|
-
? role +
|
|
107
|
-
' dal tercihi: ' +
|
|
108
|
-
(value.branch ? value.remote + '/' + value.branch : 'henüz yok') +
|
|
109
|
-
'. Ortak proje ayarını kaydedelim mi?'
|
|
110
|
-
: 'Save the shared ' +
|
|
111
|
-
role +
|
|
112
|
-
' branch preference: ' +
|
|
113
|
-
(value.branch ? value.remote + '/' + value.branch : 'none yet') +
|
|
114
|
-
'?';
|
|
271
|
+
return value.branch ? value.remote + '/' + value.branch : none;
|
|
115
272
|
};
|
|
116
273
|
const digest = sha256(stableStringify({
|
|
117
274
|
projectId: input.projectId,
|
|
@@ -121,29 +278,44 @@ export function registerWorktreeTools(server, service) {
|
|
|
121
278
|
...(input.decisionAttempt ? { decisionAttempt: input.decisionAttempt } : {}),
|
|
122
279
|
}));
|
|
123
280
|
const questionnaireId = 'project-branches-' + digest;
|
|
124
|
-
const definition = {
|
|
125
|
-
|
|
281
|
+
const definition = (turkish) => {
|
|
282
|
+
const copy = preferenceCopy[turkish ? 'tr' : 'en'];
|
|
283
|
+
return {
|
|
284
|
+
questionnaireId,
|
|
285
|
+
language: turkish ? 'tr' : 'en',
|
|
286
|
+
message: copy.message,
|
|
287
|
+
context: copy.context,
|
|
288
|
+
example: copy.example,
|
|
289
|
+
questions: roles.map((role) => ({
|
|
290
|
+
id: role,
|
|
291
|
+
message: copy.ask(copy.roles[role], source(role, null)),
|
|
292
|
+
options: [
|
|
293
|
+
{ id: 'approve', label: copy.save, description: copy.saved },
|
|
294
|
+
{ id: 'defer', label: copy.leave, description: copy.unchanged },
|
|
295
|
+
],
|
|
296
|
+
})),
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
const previous = (turkish) => ({
|
|
126
300
|
questionnaireId,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
message: input.language === 'tr'
|
|
301
|
+
language: turkish ? 'tr' : 'en',
|
|
302
|
+
message: turkish
|
|
130
303
|
? 'Ortak dal tercihlerini kaydedelim mi? Hiçbir şey kaydedilmeden önce onayını bekliyor.'
|
|
131
304
|
: 'Save the shared branch preferences? Nothing is saved until you answer.',
|
|
132
305
|
questions: roles.map((role) => ({
|
|
133
306
|
id: role,
|
|
134
|
-
message:
|
|
307
|
+
message: turkish
|
|
308
|
+
? role +
|
|
309
|
+
' dal tercihi: ' +
|
|
310
|
+
source(role, 'henüz yok') +
|
|
311
|
+
'. Ortak proje ayarını kaydedelim mi?'
|
|
312
|
+
: 'Save the shared ' + role + ' branch preference: ' + source(role, 'none yet') + '?',
|
|
135
313
|
options: [
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
label: input.language === 'tr' ? 'Bu tercihi kaydet' : 'Save this choice',
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
id: 'defer',
|
|
142
|
-
label: input.language === 'tr' ? 'Şimdilik değiştirme' : 'Leave unchanged',
|
|
143
|
-
},
|
|
314
|
+
{ id: 'approve', label: turkish ? 'Bu tercihi kaydet' : 'Save this choice' },
|
|
315
|
+
{ id: 'defer', label: turkish ? 'Şimdilik değiştirme' : 'Leave unchanged' },
|
|
144
316
|
],
|
|
145
317
|
})),
|
|
146
|
-
};
|
|
318
|
+
});
|
|
147
319
|
const owner = {
|
|
148
320
|
tool: 'project.set_git_preferences',
|
|
149
321
|
...(input.decisionAttempt ? { decisionAttempt: input.decisionAttempt } : {}),
|
|
@@ -156,7 +328,11 @@ export function registerWorktreeTools(server, service) {
|
|
|
156
328
|
language: input.language,
|
|
157
329
|
}),
|
|
158
330
|
};
|
|
159
|
-
const form = await askQuestionnaire(server, service,
|
|
331
|
+
const form = await askQuestionnaire(server, service, {
|
|
332
|
+
...definition(language === 'tr'),
|
|
333
|
+
repoRoot: input.repoRoot,
|
|
334
|
+
presentation: input.presentation,
|
|
335
|
+
}, context, [definition(language !== 'tr'), previous(true), previous(false)], owner);
|
|
160
336
|
const record = await service.questionnaireResume({
|
|
161
337
|
repoRoot: input.repoRoot,
|
|
162
338
|
questionnaireId,
|
|
@@ -188,10 +364,11 @@ export function registerWorktreeTools(server, service) {
|
|
|
188
364
|
name: branch.optional(),
|
|
189
365
|
base: baseSchema.optional(),
|
|
190
366
|
keepCurrent: z.boolean().optional(),
|
|
191
|
-
language:
|
|
367
|
+
language: languageTag.optional(),
|
|
192
368
|
presentation,
|
|
193
369
|
}),
|
|
194
370
|
}, async (input, context) => {
|
|
371
|
+
const language = (await service.language(input.language))?.startsWith('tr') ? 'tr' : 'en';
|
|
195
372
|
const preflight = await service.taskBranchPreflight(input);
|
|
196
373
|
if (!preflight.ok)
|
|
197
374
|
return output(preflight);
|
|
@@ -210,7 +387,7 @@ export function registerWorktreeTools(server, service) {
|
|
|
210
387
|
recovery: 'task.branch',
|
|
211
388
|
},
|
|
212
389
|
});
|
|
213
|
-
const definition = taskStartDefinition({ ...input, ...flight });
|
|
390
|
+
const { previous, ...definition } = taskStartDefinition({ ...input, ...flight, language });
|
|
214
391
|
const owner = {
|
|
215
392
|
tool: 'task.branch',
|
|
216
393
|
externalTaskId: input.externalTaskId,
|
|
@@ -222,7 +399,7 @@ export function registerWorktreeTools(server, service) {
|
|
|
222
399
|
keepCurrent: input.keepCurrent,
|
|
223
400
|
}),
|
|
224
401
|
};
|
|
225
|
-
const form = await askQuestionnaire(server, service, { ...definition, repoRoot: input.repoRoot, presentation: input.presentation }, context, [], owner);
|
|
402
|
+
const form = await askQuestionnaire(server, service, { ...definition, repoRoot: input.repoRoot, presentation: input.presentation }, context, [previous], owner);
|
|
226
403
|
const record = await service.questionnaireResume({
|
|
227
404
|
repoRoot: input.repoRoot,
|
|
228
405
|
questionnaireId: definition.questionnaireId,
|
|
@@ -270,16 +447,28 @@ export function registerWorktreeTools(server, service) {
|
|
|
270
447
|
const fallbackId = 'task-branch-fallback-' +
|
|
271
448
|
sha256(stableStringify({ ...request, base: fallbackBase, inPlace: choice.inPlace }));
|
|
272
449
|
const fallback = async () => {
|
|
273
|
-
const tr = input.language === 'tr';
|
|
274
450
|
const from = sourceLabel(choice.base, flight.currentBranch);
|
|
275
451
|
const local = sourceLabel(fallbackBase, flight.currentBranch);
|
|
276
|
-
const
|
|
277
|
-
|
|
452
|
+
const definition = (turkish) => {
|
|
453
|
+
const copy = fallbackCopy[turkish ? 'tr' : 'en'];
|
|
454
|
+
return {
|
|
455
|
+
questionnaireId: fallbackId,
|
|
456
|
+
language: turkish ? 'tr' : 'en',
|
|
457
|
+
allowFreeText: false,
|
|
458
|
+
message: copy.message(from, local, choice.inPlace),
|
|
459
|
+
context: copy.context,
|
|
460
|
+
example: copy.example(from, local),
|
|
461
|
+
options: [
|
|
462
|
+
{ id: 'approve', label: copy.approve, description: copy.approved(local) },
|
|
463
|
+
{ id: 'defer', label: copy.defer, description: copy.deferred },
|
|
464
|
+
],
|
|
465
|
+
};
|
|
466
|
+
};
|
|
467
|
+
const previousFallback = (turkish) => ({
|
|
278
468
|
questionnaireId: fallbackId,
|
|
279
|
-
language:
|
|
280
|
-
presentation: input.presentation,
|
|
469
|
+
language: turkish ? 'tr' : 'en',
|
|
281
470
|
allowFreeText: false,
|
|
282
|
-
message:
|
|
471
|
+
message: turkish
|
|
283
472
|
? from +
|
|
284
473
|
' uzak dalına ulaşılamadı. Yereldeki ' +
|
|
285
474
|
local +
|
|
@@ -292,10 +481,15 @@ export function registerWorktreeTools(server, service) {
|
|
|
292
481
|
local +
|
|
293
482
|
(choice.inPlace ? ' in this folder?' : ' in a separate worktree?'),
|
|
294
483
|
options: [
|
|
295
|
-
{ id: 'approve', label:
|
|
296
|
-
{ id: 'defer', label:
|
|
484
|
+
{ id: 'approve', label: turkish ? 'Devam et' : 'Continue' },
|
|
485
|
+
{ id: 'defer', label: turkish ? 'Şimdilik başlatma' : 'Not now' },
|
|
297
486
|
],
|
|
298
|
-
}
|
|
487
|
+
});
|
|
488
|
+
const fallbackForm = await askQuestionnaire(server, service, {
|
|
489
|
+
...definition(language === 'tr'),
|
|
490
|
+
repoRoot: input.repoRoot,
|
|
491
|
+
presentation: input.presentation,
|
|
492
|
+
}, context, [definition(language !== 'tr'), previousFallback(true), previousFallback(false)], owner);
|
|
299
493
|
const answer = await service.questionnaireResume({
|
|
300
494
|
repoRoot: input.repoRoot,
|
|
301
495
|
questionnaireId: fallbackId,
|
|
@@ -344,9 +538,10 @@ export function registerWorktreeTools(server, service) {
|
|
|
344
538
|
retireLegacy: z.boolean().optional(),
|
|
345
539
|
forgetUnreadable: z.strictObject({ externalTaskId: z.string().min(2).max(160) }).optional(),
|
|
346
540
|
decisionAttempt: z.number().int().min(0).default(0),
|
|
347
|
-
language:
|
|
541
|
+
language: languageTag.optional(),
|
|
348
542
|
}),
|
|
349
543
|
}, async (input, context) => {
|
|
544
|
+
const language = (await service.language(input.language))?.startsWith('tr') ? 'tr' : 'en';
|
|
350
545
|
const inspection = await service.worktreeOperation('reconcile', input);
|
|
351
546
|
if (!inspection.ok ||
|
|
352
547
|
(!input.releaseUnowned && !input.retireLegacy && !input.forgetUnreadable))
|
|
@@ -365,13 +560,26 @@ export function registerWorktreeTools(server, service) {
|
|
|
365
560
|
});
|
|
366
561
|
const questionnaireId = forgetUnreadableQuestionId(item, input.decisionAttempt);
|
|
367
562
|
const folder = basename(item.repoRoot);
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
563
|
+
const definition = (turkish) => {
|
|
564
|
+
const copy = forgetCopy[turkish ? 'tr' : 'en'];
|
|
565
|
+
return {
|
|
566
|
+
questionnaireId,
|
|
567
|
+
language: turkish ? 'tr' : 'en',
|
|
568
|
+
allowFreeText: false,
|
|
569
|
+
message: copy.message(item.externalTaskId, folder),
|
|
570
|
+
context: copy.context(item.pendingDelivery === true),
|
|
571
|
+
example: copy.example(item.externalTaskId, folder),
|
|
572
|
+
options: [
|
|
573
|
+
{ id: 'forget', label: copy.forget, description: copy.forgotten },
|
|
574
|
+
{ id: 'keep', label: copy.keep, description: copy.kept },
|
|
575
|
+
],
|
|
576
|
+
};
|
|
577
|
+
};
|
|
578
|
+
const previous = (turkish) => ({
|
|
371
579
|
questionnaireId,
|
|
372
|
-
language:
|
|
580
|
+
language: turkish ? 'tr' : 'en',
|
|
373
581
|
allowFreeText: false,
|
|
374
|
-
message:
|
|
582
|
+
message: turkish
|
|
375
583
|
? item.externalTaskId +
|
|
376
584
|
' görevinin ' +
|
|
377
585
|
folder +
|
|
@@ -390,13 +598,14 @@ export function registerWorktreeTools(server, service) {
|
|
|
390
598
|
options: [
|
|
391
599
|
{
|
|
392
600
|
id: 'forget',
|
|
393
|
-
label:
|
|
601
|
+
label: turkish
|
|
394
602
|
? 'Takip etmeyi bırak; dosyalara dokunma'
|
|
395
603
|
: 'Stop tracking it; leave the files alone',
|
|
396
604
|
},
|
|
397
|
-
{ id: 'keep', label:
|
|
605
|
+
{ id: 'keep', label: turkish ? 'Takip etmeye devam et' : 'Keep tracking it' },
|
|
398
606
|
],
|
|
399
|
-
}
|
|
607
|
+
});
|
|
608
|
+
const form = await askQuestionnaire(server, service, { ...definition(language === 'tr'), repoRoot: input.repoRoot }, context, [definition(language !== 'tr'), previous(true), previous(false)], {
|
|
400
609
|
tool: 'worktree.reconcile',
|
|
401
610
|
...(input.decisionAttempt ? { decisionAttempt: input.decisionAttempt } : {}),
|
|
402
611
|
retryArguments: json({
|
|
@@ -429,12 +638,27 @@ export function registerWorktreeTools(server, service) {
|
|
|
429
638
|
const questionnaireId = 'worktree-retire-' + sha256(item.repoRoot + item.generation);
|
|
430
639
|
const missing = item.reasons.includes('worktree directory missing');
|
|
431
640
|
const folder = basename(item.repoRoot);
|
|
432
|
-
const
|
|
433
|
-
|
|
641
|
+
const definition = (turkish) => {
|
|
642
|
+
const copy = retireCopy[turkish ? 'tr' : 'en'];
|
|
643
|
+
const named = item.branch ?? (turkish ? 'yok' : 'none');
|
|
644
|
+
return {
|
|
645
|
+
questionnaireId,
|
|
646
|
+
language: turkish ? 'tr' : 'en',
|
|
647
|
+
allowFreeText: false,
|
|
648
|
+
message: missing ? copy.missing(folder) : copy.message(folder, named),
|
|
649
|
+
context: copy.context(missing),
|
|
650
|
+
example: copy.example(folder, named, missing),
|
|
651
|
+
options: [
|
|
652
|
+
{ id: 'retire', label: copy.retire, description: copy.retired(missing) },
|
|
653
|
+
{ id: 'keep', label: copy.keep, description: copy.kept },
|
|
654
|
+
],
|
|
655
|
+
};
|
|
656
|
+
};
|
|
657
|
+
const previous = (turkish) => ({
|
|
434
658
|
questionnaireId,
|
|
435
|
-
language:
|
|
659
|
+
language: turkish ? 'tr' : 'en',
|
|
436
660
|
allowFreeText: false,
|
|
437
|
-
message:
|
|
661
|
+
message: turkish
|
|
438
662
|
? folder +
|
|
439
663
|
(missing
|
|
440
664
|
? ' eski konumdaki bir worktree kaydı; klasör artık yok. Kaydı silinsin mi?'
|
|
@@ -448,13 +672,11 @@ export function registerWorktreeTools(server, service) {
|
|
|
448
672
|
(item.branch ?? 'none') +
|
|
449
673
|
'); it is no longer reused. Remove it? The branch stays. Git removes only a clean folder, and its ignored files go with it.'),
|
|
450
674
|
options: [
|
|
451
|
-
{
|
|
452
|
-
|
|
453
|
-
label: input.language === 'tr' ? 'Klasörü kaldır' : 'Remove the folder',
|
|
454
|
-
},
|
|
455
|
-
{ id: 'keep', label: input.language === 'tr' ? 'Kalsın' : 'Keep it' },
|
|
675
|
+
{ id: 'retire', label: turkish ? 'Klasörü kaldır' : 'Remove the folder' },
|
|
676
|
+
{ id: 'keep', label: turkish ? 'Kalsın' : 'Keep it' },
|
|
456
677
|
],
|
|
457
|
-
}
|
|
678
|
+
});
|
|
679
|
+
const form = await askQuestionnaire(server, service, { ...definition(language === 'tr'), repoRoot: input.repoRoot }, context, [definition(language !== 'tr'), previous(true), previous(false)]);
|
|
458
680
|
const decision = await service.questionnaireResume({
|
|
459
681
|
repoRoot: input.repoRoot,
|
|
460
682
|
questionnaireId,
|
|
@@ -498,27 +720,39 @@ export function registerWorktreeTools(server, service) {
|
|
|
498
720
|
decisionAttempt: input.decisionAttempt,
|
|
499
721
|
}));
|
|
500
722
|
const questionnaireId = 'worktree-release-unowned-' + digest;
|
|
501
|
-
const
|
|
502
|
-
|
|
723
|
+
const definition = (turkish) => {
|
|
724
|
+
const copy = unownedCopy[turkish ? 'tr' : 'en'];
|
|
725
|
+
return {
|
|
726
|
+
questionnaireId,
|
|
727
|
+
language: turkish ? 'tr' : 'en',
|
|
728
|
+
allowFreeText: false,
|
|
729
|
+
message: copy.message(basename(entry.repoRoot)),
|
|
730
|
+
context: copy.context,
|
|
731
|
+
example: copy.example(entry.baseCommit.slice(0, 12)),
|
|
732
|
+
options: [
|
|
733
|
+
{ id: 'release_unowned', label: copy.release, description: copy.released },
|
|
734
|
+
{ id: 'preserve', label: copy.preserve, description: copy.preserved },
|
|
735
|
+
],
|
|
736
|
+
};
|
|
737
|
+
};
|
|
738
|
+
const previous = (turkish) => ({
|
|
503
739
|
questionnaireId,
|
|
504
|
-
language:
|
|
740
|
+
language: turkish ? 'tr' : 'en',
|
|
505
741
|
allowFreeText: false,
|
|
506
|
-
message:
|
|
742
|
+
message: turkish
|
|
507
743
|
? 'Bu eski çalışma klasörünün önceki sahiplik kaydı yok. Önceki ajan burada artık çalışmıyor ve teslim tamamlandıysa, temiz klasörü yeniden kullanılabilir yapalım mı? Dosyalar ve branch silinmeyecek.'
|
|
508
744
|
: 'This legacy checkout has no prior ownership record. If the previous agent has stopped and delivery is complete, make the clean checkout reusable? No files or branches will be deleted.',
|
|
509
745
|
options: [
|
|
510
746
|
{
|
|
511
747
|
id: 'release_unowned',
|
|
512
|
-
label:
|
|
748
|
+
label: turkish
|
|
513
749
|
? 'Önceki iş bitti; güvenli kontrollerden sonra kullanılabilir yap'
|
|
514
750
|
: 'Prior work finished; release after safety checks',
|
|
515
751
|
},
|
|
516
|
-
{
|
|
517
|
-
id: 'preserve',
|
|
518
|
-
label: input.language === 'tr' ? 'Korumaya devam et' : 'Keep protected',
|
|
519
|
-
},
|
|
752
|
+
{ id: 'preserve', label: turkish ? 'Korumaya devam et' : 'Keep protected' },
|
|
520
753
|
],
|
|
521
|
-
}
|
|
754
|
+
});
|
|
755
|
+
const form = await askQuestionnaire(server, service, { ...definition(language === 'tr'), repoRoot: input.repoRoot }, context, [definition(language !== 'tr'), previous(true), previous(false)]);
|
|
522
756
|
const decision = await service.questionnaireResume({
|
|
523
757
|
repoRoot: input.repoRoot,
|
|
524
758
|
questionnaireId,
|
|
@@ -546,9 +780,10 @@ export function registerWorktreeTools(server, service) {
|
|
|
546
780
|
inputSchema: ownershipSchema.extend({
|
|
547
781
|
deliveryOutcome: z.enum(['delivered', 'cancelled']).optional(),
|
|
548
782
|
delivered: z.boolean().optional(),
|
|
549
|
-
language:
|
|
783
|
+
language: languageTag.optional(),
|
|
550
784
|
}),
|
|
551
785
|
}, async (input, context) => {
|
|
786
|
+
const language = (await service.language(input.language))?.startsWith('tr') ? 'tr' : 'en';
|
|
552
787
|
if (input.deliveryOutcome !== 'cancelled')
|
|
553
788
|
return output(await service.worktreeOperation('release', input));
|
|
554
789
|
const state = await service.worktreeFolder(input);
|
|
@@ -561,28 +796,26 @@ export function registerWorktreeTools(server, service) {
|
|
|
561
796
|
...input,
|
|
562
797
|
decisionId: questionnaireId,
|
|
563
798
|
}));
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
label:
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
:
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
label: input.language === 'tr' ? 'Klasör olduğu gibi kalsın' : 'Keep the folder as it is',
|
|
799
|
+
const definition = (turkish) => {
|
|
800
|
+
const copy = cancelCopy[turkish ? 'tr' : 'en'];
|
|
801
|
+
const keep = { id: 'keep', label: copy.keep, description: copy.kept };
|
|
802
|
+
return {
|
|
803
|
+
questionnaireId,
|
|
804
|
+
language: turkish ? 'tr' : 'en',
|
|
805
|
+
allowFreeText: false,
|
|
806
|
+
message: copy.message(input.externalTaskId),
|
|
807
|
+
context: copy.context(folder.clean, folder.managed),
|
|
808
|
+
example: copy.example,
|
|
809
|
+
options: folder.clean || !folder.managed
|
|
810
|
+
? [{ id: 'release', label: copy.release, description: copy.released }, keep]
|
|
811
|
+
: [{ id: 'save_and_release', label: copy.save, description: copy.saved }, keep],
|
|
812
|
+
};
|
|
579
813
|
};
|
|
580
|
-
const
|
|
581
|
-
repoRoot: input.repoRoot,
|
|
814
|
+
const previous = (turkish) => ({
|
|
582
815
|
questionnaireId,
|
|
583
|
-
language:
|
|
816
|
+
language: turkish ? 'tr' : 'en',
|
|
584
817
|
allowFreeText: false,
|
|
585
|
-
message:
|
|
818
|
+
message: turkish
|
|
586
819
|
? input.externalTaskId +
|
|
587
820
|
' teslim edilmeyecek. Bu klasör ne olsun?' +
|
|
588
821
|
(folder.clean
|
|
@@ -597,8 +830,33 @@ export function registerWorktreeTools(server, service) {
|
|
|
597
830
|
: folder.managed
|
|
598
831
|
? ' It has uncommitted changes; saving keeps them in a Git ref they can be restored from.'
|
|
599
832
|
: ' It has uncommitted changes; this is your own folder, so its files are never touched when it is freed.'),
|
|
600
|
-
options: folder.clean || !folder.managed
|
|
601
|
-
|
|
833
|
+
options: folder.clean || !folder.managed
|
|
834
|
+
? [
|
|
835
|
+
{
|
|
836
|
+
id: 'release',
|
|
837
|
+
label: turkish
|
|
838
|
+
? 'Klasörü boşalt; branch kalsın'
|
|
839
|
+
: 'Free the folder; the branch stays',
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
id: 'keep',
|
|
843
|
+
label: turkish ? 'Klasör olduğu gibi kalsın' : 'Keep the folder as it is',
|
|
844
|
+
},
|
|
845
|
+
]
|
|
846
|
+
: [
|
|
847
|
+
{
|
|
848
|
+
id: 'save_and_release',
|
|
849
|
+
label: turkish
|
|
850
|
+
? 'Değişiklikleri kaydedip klasörü boşalt'
|
|
851
|
+
: 'Save the changes and free the folder',
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
id: 'keep',
|
|
855
|
+
label: turkish ? 'Klasör olduğu gibi kalsın' : 'Keep the folder as it is',
|
|
856
|
+
},
|
|
857
|
+
],
|
|
858
|
+
});
|
|
859
|
+
const form = await askQuestionnaire(server, service, { ...definition(language === 'tr'), repoRoot: input.repoRoot }, context, [definition(language !== 'tr'), previous(true), previous(false)]);
|
|
602
860
|
const decision = await service.questionnaireResume({
|
|
603
861
|
repoRoot: input.repoRoot,
|
|
604
862
|
questionnaireId,
|