engineering-memory 1.11.10 → 1.11.12
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 +3 -2
- package/package.json +1 -1
- package/runtime/build.json +1 -1
- package/runtime/dist/src/config.js +1 -0
- package/runtime/dist/src/git/git-inspector.js +32 -1
- package/runtime/dist/src/mcp/onboarding-tools.js +918 -132
- package/runtime/dist/src/mcp/questionnaire-tools.js +55 -16
- package/runtime/dist/src/mcp/tool-annotations.js +1 -0
- package/runtime/dist/src/mcp/tool-definitions.js +105 -17
- package/runtime/dist/src/mcp/worktree-tools.js +416 -86
- package/runtime/dist/src/project/repository.js +15 -3
- package/runtime/dist/src/runtime/api-client.js +1 -0
- package/runtime/dist/src/runtime/bridge-service.js +308 -46
- 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/runtime/dist/src/runtime/worktree-pool.js +41 -3
- package/skill/references/lifecycle.md +15 -0
- package/skill/references/questionnaires.md +17 -3
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { basename } from 'node:path';
|
|
2
2
|
import * as z from 'zod/v4';
|
|
3
|
-
import { cancelledDeliveryQuestionId, } from '../runtime/bridge-service.js';
|
|
3
|
+
import { cancelledDeliveryQuestionId, forgetUnreadableQuestionId, } from '../runtime/bridge-service.js';
|
|
4
4
|
import { configuredBase, sourceLabel, taskStartChoice, taskStartDefinition, } from '../runtime/task-start.js';
|
|
5
5
|
import { sha256, stableStringify } from '../utilities/hash.js';
|
|
6
|
+
import { unreadableWorktree } from '../runtime/worktree-pool.js';
|
|
6
7
|
import { askQuestionnaire } from './questionnaire-tools.js';
|
|
8
|
+
import { languageTag } from '../runtime/questionnaire-store.js';
|
|
7
9
|
const repo = z.string().min(1);
|
|
8
10
|
const branch = z.string().min(1).max(255);
|
|
9
11
|
const remote = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/);
|
|
@@ -48,6 +50,170 @@ const policy = {
|
|
|
48
50
|
heartbeatSeconds: z.number().int().min(5).max(300),
|
|
49
51
|
inactivitySeconds: z.number().int().min(60).max(86400),
|
|
50
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
|
+
};
|
|
51
217
|
const baseSchema = z.discriminatedUnion('kind', [
|
|
52
218
|
z.strictObject({
|
|
53
219
|
kind: z.literal('current'),
|
|
@@ -78,10 +244,11 @@ export function registerWorktreeTools(server, service) {
|
|
|
78
244
|
development: preference.optional(),
|
|
79
245
|
production: preference.optional(),
|
|
80
246
|
test: preference.optional(),
|
|
81
|
-
language:
|
|
247
|
+
language: languageTag.optional(),
|
|
82
248
|
presentation,
|
|
83
249
|
}),
|
|
84
250
|
}, async (input, context) => {
|
|
251
|
+
const language = (await service.language(input.language))?.startsWith('tr') ? 'tr' : 'en';
|
|
85
252
|
const current = await service.projectGitPreferences(input);
|
|
86
253
|
if (!current.ok)
|
|
87
254
|
return output(current);
|
|
@@ -99,18 +266,9 @@ export function registerWorktreeTools(server, service) {
|
|
|
99
266
|
const roles = ['development', 'production', 'test'].filter((role) => Boolean(input[role]));
|
|
100
267
|
if (roles.length === 0)
|
|
101
268
|
return output({ ok: true, data: { unchanged: true } });
|
|
102
|
-
const
|
|
269
|
+
const source = (role, none) => {
|
|
103
270
|
const value = input[role];
|
|
104
|
-
return
|
|
105
|
-
? role +
|
|
106
|
-
' dal tercihi: ' +
|
|
107
|
-
(value.branch ? value.remote + '/' + value.branch : 'henüz yok') +
|
|
108
|
-
'. Ortak proje ayarını kaydedelim mi?'
|
|
109
|
-
: 'Save the shared ' +
|
|
110
|
-
role +
|
|
111
|
-
' branch preference: ' +
|
|
112
|
-
(value.branch ? value.remote + '/' + value.branch : 'none yet') +
|
|
113
|
-
'?';
|
|
271
|
+
return value.branch ? value.remote + '/' + value.branch : none;
|
|
114
272
|
};
|
|
115
273
|
const digest = sha256(stableStringify({
|
|
116
274
|
projectId: input.projectId,
|
|
@@ -120,29 +278,44 @@ export function registerWorktreeTools(server, service) {
|
|
|
120
278
|
...(input.decisionAttempt ? { decisionAttempt: input.decisionAttempt } : {}),
|
|
121
279
|
}));
|
|
122
280
|
const questionnaireId = 'project-branches-' + digest;
|
|
123
|
-
const definition = {
|
|
124
|
-
|
|
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) => ({
|
|
125
300
|
questionnaireId,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
message: input.language === 'tr'
|
|
301
|
+
language: turkish ? 'tr' : 'en',
|
|
302
|
+
message: turkish
|
|
129
303
|
? 'Ortak dal tercihlerini kaydedelim mi? Hiçbir şey kaydedilmeden önce onayını bekliyor.'
|
|
130
304
|
: 'Save the shared branch preferences? Nothing is saved until you answer.',
|
|
131
305
|
questions: roles.map((role) => ({
|
|
132
306
|
id: role,
|
|
133
|
-
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') + '?',
|
|
134
313
|
options: [
|
|
135
|
-
{
|
|
136
|
-
|
|
137
|
-
label: input.language === 'tr' ? 'Bu tercihi kaydet' : 'Save this choice',
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
id: 'defer',
|
|
141
|
-
label: input.language === 'tr' ? 'Şimdilik değiştirme' : 'Leave unchanged',
|
|
142
|
-
},
|
|
314
|
+
{ id: 'approve', label: turkish ? 'Bu tercihi kaydet' : 'Save this choice' },
|
|
315
|
+
{ id: 'defer', label: turkish ? 'Şimdilik değiştirme' : 'Leave unchanged' },
|
|
143
316
|
],
|
|
144
317
|
})),
|
|
145
|
-
};
|
|
318
|
+
});
|
|
146
319
|
const owner = {
|
|
147
320
|
tool: 'project.set_git_preferences',
|
|
148
321
|
...(input.decisionAttempt ? { decisionAttempt: input.decisionAttempt } : {}),
|
|
@@ -155,7 +328,11 @@ export function registerWorktreeTools(server, service) {
|
|
|
155
328
|
language: input.language,
|
|
156
329
|
}),
|
|
157
330
|
};
|
|
158
|
-
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);
|
|
159
336
|
const record = await service.questionnaireResume({
|
|
160
337
|
repoRoot: input.repoRoot,
|
|
161
338
|
questionnaireId,
|
|
@@ -187,10 +364,11 @@ export function registerWorktreeTools(server, service) {
|
|
|
187
364
|
name: branch.optional(),
|
|
188
365
|
base: baseSchema.optional(),
|
|
189
366
|
keepCurrent: z.boolean().optional(),
|
|
190
|
-
language:
|
|
367
|
+
language: languageTag.optional(),
|
|
191
368
|
presentation,
|
|
192
369
|
}),
|
|
193
370
|
}, async (input, context) => {
|
|
371
|
+
const language = (await service.language(input.language))?.startsWith('tr') ? 'tr' : 'en';
|
|
194
372
|
const preflight = await service.taskBranchPreflight(input);
|
|
195
373
|
if (!preflight.ok)
|
|
196
374
|
return output(preflight);
|
|
@@ -209,7 +387,7 @@ export function registerWorktreeTools(server, service) {
|
|
|
209
387
|
recovery: 'task.branch',
|
|
210
388
|
},
|
|
211
389
|
});
|
|
212
|
-
const definition = taskStartDefinition({ ...input, ...flight });
|
|
390
|
+
const { previous, ...definition } = taskStartDefinition({ ...input, ...flight, language });
|
|
213
391
|
const owner = {
|
|
214
392
|
tool: 'task.branch',
|
|
215
393
|
externalTaskId: input.externalTaskId,
|
|
@@ -221,7 +399,7 @@ export function registerWorktreeTools(server, service) {
|
|
|
221
399
|
keepCurrent: input.keepCurrent,
|
|
222
400
|
}),
|
|
223
401
|
};
|
|
224
|
-
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);
|
|
225
403
|
const record = await service.questionnaireResume({
|
|
226
404
|
repoRoot: input.repoRoot,
|
|
227
405
|
questionnaireId: definition.questionnaireId,
|
|
@@ -269,16 +447,28 @@ export function registerWorktreeTools(server, service) {
|
|
|
269
447
|
const fallbackId = 'task-branch-fallback-' +
|
|
270
448
|
sha256(stableStringify({ ...request, base: fallbackBase, inPlace: choice.inPlace }));
|
|
271
449
|
const fallback = async () => {
|
|
272
|
-
const tr = input.language === 'tr';
|
|
273
450
|
const from = sourceLabel(choice.base, flight.currentBranch);
|
|
274
451
|
const local = sourceLabel(fallbackBase, flight.currentBranch);
|
|
275
|
-
const
|
|
276
|
-
|
|
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) => ({
|
|
277
468
|
questionnaireId: fallbackId,
|
|
278
|
-
language:
|
|
279
|
-
presentation: input.presentation,
|
|
469
|
+
language: turkish ? 'tr' : 'en',
|
|
280
470
|
allowFreeText: false,
|
|
281
|
-
message:
|
|
471
|
+
message: turkish
|
|
282
472
|
? from +
|
|
283
473
|
' uzak dalına ulaşılamadı. Yereldeki ' +
|
|
284
474
|
local +
|
|
@@ -291,10 +481,15 @@ export function registerWorktreeTools(server, service) {
|
|
|
291
481
|
local +
|
|
292
482
|
(choice.inPlace ? ' in this folder?' : ' in a separate worktree?'),
|
|
293
483
|
options: [
|
|
294
|
-
{ id: 'approve', label:
|
|
295
|
-
{ id: 'defer', label:
|
|
484
|
+
{ id: 'approve', label: turkish ? 'Devam et' : 'Continue' },
|
|
485
|
+
{ id: 'defer', label: turkish ? 'Şimdilik başlatma' : 'Not now' },
|
|
296
486
|
],
|
|
297
|
-
}
|
|
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);
|
|
298
493
|
const answer = await service.questionnaireResume({
|
|
299
494
|
repoRoot: input.repoRoot,
|
|
300
495
|
questionnaireId: fallbackId,
|
|
@@ -336,18 +531,104 @@ export function registerWorktreeTools(server, service) {
|
|
|
336
531
|
inputSchema: locationSchema,
|
|
337
532
|
}, async (input) => output(await service.worktreeOperation('list', input)));
|
|
338
533
|
server.registerTool('worktree.reconcile', {
|
|
339
|
-
description: 'Reconcile Git with the local registry, including unregistered legacy checkouts. Use releaseUnowned only on a selected unreserved recovered checkout after prior work has finished; a native source-bound confirmation and fresh safety checks precede reuse. Use retireLegacy only when the user wants old-location slots gone: each retired folder is removed with git worktree remove after its own native approval, and the branch stays. Otherwise never delete files or reset branches.',
|
|
534
|
+
description: 'Reconcile Git with the local registry, including unregistered legacy checkouts. Use releaseUnowned only on a selected unreserved recovered checkout after prior work has finished; a native source-bound confirmation and fresh safety checks precede reuse. Use retireLegacy only when the user wants old-location slots gone: each retired folder is removed with git worktree remove after its own native approval, and the branch stays. Use forgetUnreadable with the externalTaskId of a checkout that is gone or can no longer be read as a Git worktree, called from a working checkout of the same project: after its own native approval Engineering Memory stops tracking that entry, so it no longer counts toward the limit, and no file in the folder is read, moved or deleted. Otherwise never delete files or reset branches.',
|
|
340
535
|
inputSchema: z.strictObject({
|
|
341
536
|
repoRoot: repo,
|
|
342
537
|
releaseUnowned: z.boolean().optional(),
|
|
343
538
|
retireLegacy: z.boolean().optional(),
|
|
539
|
+
forgetUnreadable: z.strictObject({ externalTaskId: z.string().min(2).max(160) }).optional(),
|
|
344
540
|
decisionAttempt: z.number().int().min(0).default(0),
|
|
345
|
-
language:
|
|
541
|
+
language: languageTag.optional(),
|
|
346
542
|
}),
|
|
347
543
|
}, async (input, context) => {
|
|
544
|
+
const language = (await service.language(input.language))?.startsWith('tr') ? 'tr' : 'en';
|
|
348
545
|
const inspection = await service.worktreeOperation('reconcile', input);
|
|
349
|
-
if (!inspection.ok ||
|
|
546
|
+
if (!inspection.ok ||
|
|
547
|
+
(!input.releaseUnowned && !input.retireLegacy && !input.forgetUnreadable))
|
|
350
548
|
return output(inspection);
|
|
549
|
+
if (input.forgetUnreadable) {
|
|
550
|
+
const item = inspection.data.items.find((entry) => entry.externalTaskId === input.forgetUnreadable.externalTaskId);
|
|
551
|
+
if (!item || item.activity === 'retired' || !unreadableWorktree(item))
|
|
552
|
+
return output({
|
|
553
|
+
ok: false,
|
|
554
|
+
error: {
|
|
555
|
+
kind: 'bridge_error',
|
|
556
|
+
retryable: false,
|
|
557
|
+
recovery: 'worktree.list',
|
|
558
|
+
message: 'Only a checkout that is missing or can no longer be read as a Git worktree is forgotten. Call worktree.list to see this task: a readable one is freed with worktree.release, and a retired slot is removed with worktree.reconcile and retireLegacy.',
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
const questionnaireId = forgetUnreadableQuestionId(item, input.decisionAttempt);
|
|
562
|
+
const folder = basename(item.repoRoot);
|
|
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) => ({
|
|
579
|
+
questionnaireId,
|
|
580
|
+
language: turkish ? 'tr' : 'en',
|
|
581
|
+
allowFreeText: false,
|
|
582
|
+
message: turkish
|
|
583
|
+
? item.externalTaskId +
|
|
584
|
+
' görevinin ' +
|
|
585
|
+
folder +
|
|
586
|
+
' klasörü artık bir Git çalışma kopyası olarak okunamıyor. Engineering Memory bu klasörü takip etmeyi bıraksın mı? Klasördeki hiçbir dosyaya dokunulmaz, hiçbiri silinmez.' +
|
|
587
|
+
(item.pendingDelivery
|
|
588
|
+
? ' Bu görev teslimi sonuçlanmadan kapandı; teslim edilmemiş iş varsa yalnızca bu klasörde duruyor.'
|
|
589
|
+
: '')
|
|
590
|
+
: 'The ' +
|
|
591
|
+
folder +
|
|
592
|
+
' folder of task ' +
|
|
593
|
+
item.externalTaskId +
|
|
594
|
+
' can no longer be read as a Git checkout. Should Engineering Memory stop tracking it? No file in the folder is touched or deleted.' +
|
|
595
|
+
(item.pendingDelivery
|
|
596
|
+
? ' That task closed without its delivery being settled, so any undelivered work exists only in that folder.'
|
|
597
|
+
: ''),
|
|
598
|
+
options: [
|
|
599
|
+
{
|
|
600
|
+
id: 'forget',
|
|
601
|
+
label: turkish
|
|
602
|
+
? 'Takip etmeyi bırak; dosyalara dokunma'
|
|
603
|
+
: 'Stop tracking it; leave the files alone',
|
|
604
|
+
},
|
|
605
|
+
{ id: 'keep', label: turkish ? 'Takip etmeye devam et' : 'Keep tracking it' },
|
|
606
|
+
],
|
|
607
|
+
});
|
|
608
|
+
const form = await askQuestionnaire(server, service, { ...definition(language === 'tr'), repoRoot: input.repoRoot }, context, [definition(language !== 'tr'), previous(true), previous(false)], {
|
|
609
|
+
tool: 'worktree.reconcile',
|
|
610
|
+
...(input.decisionAttempt ? { decisionAttempt: input.decisionAttempt } : {}),
|
|
611
|
+
retryArguments: json({
|
|
612
|
+
forgetUnreadable: input.forgetUnreadable,
|
|
613
|
+
language: input.language,
|
|
614
|
+
}),
|
|
615
|
+
});
|
|
616
|
+
const decision = await service.questionnaireResume({
|
|
617
|
+
repoRoot: input.repoRoot,
|
|
618
|
+
questionnaireId,
|
|
619
|
+
});
|
|
620
|
+
if (decision.status !== 'answered')
|
|
621
|
+
return form;
|
|
622
|
+
if (decision.answer?.choice !== 'forget')
|
|
623
|
+
return output({ ok: true, data: json({ kept: item.externalTaskId }) });
|
|
624
|
+
return output(await service.worktreeOperation('forget_unreadable', {
|
|
625
|
+
repoRoot: input.repoRoot,
|
|
626
|
+
externalTaskId: item.externalTaskId,
|
|
627
|
+
generation: item.generation,
|
|
628
|
+
decisionAttempt: input.decisionAttempt,
|
|
629
|
+
decisionId: questionnaireId,
|
|
630
|
+
}));
|
|
631
|
+
}
|
|
351
632
|
if (input.retireLegacy) {
|
|
352
633
|
const items = inspection.data.items.filter((item) => item.activity === 'retired');
|
|
353
634
|
const retired = [];
|
|
@@ -357,12 +638,27 @@ export function registerWorktreeTools(server, service) {
|
|
|
357
638
|
const questionnaireId = 'worktree-retire-' + sha256(item.repoRoot + item.generation);
|
|
358
639
|
const missing = item.reasons.includes('worktree directory missing');
|
|
359
640
|
const folder = basename(item.repoRoot);
|
|
360
|
-
const
|
|
361
|
-
|
|
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) => ({
|
|
362
658
|
questionnaireId,
|
|
363
|
-
language:
|
|
659
|
+
language: turkish ? 'tr' : 'en',
|
|
364
660
|
allowFreeText: false,
|
|
365
|
-
message:
|
|
661
|
+
message: turkish
|
|
366
662
|
? folder +
|
|
367
663
|
(missing
|
|
368
664
|
? ' eski konumdaki bir worktree kaydı; klasör artık yok. Kaydı silinsin mi?'
|
|
@@ -376,13 +672,11 @@ export function registerWorktreeTools(server, service) {
|
|
|
376
672
|
(item.branch ?? 'none') +
|
|
377
673
|
'); it is no longer reused. Remove it? The branch stays. Git removes only a clean folder, and its ignored files go with it.'),
|
|
378
674
|
options: [
|
|
379
|
-
{
|
|
380
|
-
|
|
381
|
-
label: input.language === 'tr' ? 'Klasörü kaldır' : 'Remove the folder',
|
|
382
|
-
},
|
|
383
|
-
{ 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' },
|
|
384
677
|
],
|
|
385
|
-
}
|
|
678
|
+
});
|
|
679
|
+
const form = await askQuestionnaire(server, service, { ...definition(language === 'tr'), repoRoot: input.repoRoot }, context, [definition(language !== 'tr'), previous(true), previous(false)]);
|
|
386
680
|
const decision = await service.questionnaireResume({
|
|
387
681
|
repoRoot: input.repoRoot,
|
|
388
682
|
questionnaireId,
|
|
@@ -426,27 +720,39 @@ export function registerWorktreeTools(server, service) {
|
|
|
426
720
|
decisionAttempt: input.decisionAttempt,
|
|
427
721
|
}));
|
|
428
722
|
const questionnaireId = 'worktree-release-unowned-' + digest;
|
|
429
|
-
const
|
|
430
|
-
|
|
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) => ({
|
|
431
739
|
questionnaireId,
|
|
432
|
-
language:
|
|
740
|
+
language: turkish ? 'tr' : 'en',
|
|
433
741
|
allowFreeText: false,
|
|
434
|
-
message:
|
|
742
|
+
message: turkish
|
|
435
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.'
|
|
436
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.',
|
|
437
745
|
options: [
|
|
438
746
|
{
|
|
439
747
|
id: 'release_unowned',
|
|
440
|
-
label:
|
|
748
|
+
label: turkish
|
|
441
749
|
? 'Önceki iş bitti; güvenli kontrollerden sonra kullanılabilir yap'
|
|
442
750
|
: 'Prior work finished; release after safety checks',
|
|
443
751
|
},
|
|
444
|
-
{
|
|
445
|
-
id: 'preserve',
|
|
446
|
-
label: input.language === 'tr' ? 'Korumaya devam et' : 'Keep protected',
|
|
447
|
-
},
|
|
752
|
+
{ id: 'preserve', label: turkish ? 'Korumaya devam et' : 'Keep protected' },
|
|
448
753
|
],
|
|
449
|
-
}
|
|
754
|
+
});
|
|
755
|
+
const form = await askQuestionnaire(server, service, { ...definition(language === 'tr'), repoRoot: input.repoRoot }, context, [definition(language !== 'tr'), previous(true), previous(false)]);
|
|
450
756
|
const decision = await service.questionnaireResume({
|
|
451
757
|
repoRoot: input.repoRoot,
|
|
452
758
|
questionnaireId,
|
|
@@ -474,9 +780,10 @@ export function registerWorktreeTools(server, service) {
|
|
|
474
780
|
inputSchema: ownershipSchema.extend({
|
|
475
781
|
deliveryOutcome: z.enum(['delivered', 'cancelled']).optional(),
|
|
476
782
|
delivered: z.boolean().optional(),
|
|
477
|
-
language:
|
|
783
|
+
language: languageTag.optional(),
|
|
478
784
|
}),
|
|
479
785
|
}, async (input, context) => {
|
|
786
|
+
const language = (await service.language(input.language))?.startsWith('tr') ? 'tr' : 'en';
|
|
480
787
|
if (input.deliveryOutcome !== 'cancelled')
|
|
481
788
|
return output(await service.worktreeOperation('release', input));
|
|
482
789
|
const state = await service.worktreeFolder(input);
|
|
@@ -489,28 +796,26 @@ export function registerWorktreeTools(server, service) {
|
|
|
489
796
|
...input,
|
|
490
797
|
decisionId: questionnaireId,
|
|
491
798
|
}));
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
label:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
:
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
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
|
+
};
|
|
507
813
|
};
|
|
508
|
-
const
|
|
509
|
-
repoRoot: input.repoRoot,
|
|
814
|
+
const previous = (turkish) => ({
|
|
510
815
|
questionnaireId,
|
|
511
|
-
language:
|
|
816
|
+
language: turkish ? 'tr' : 'en',
|
|
512
817
|
allowFreeText: false,
|
|
513
|
-
message:
|
|
818
|
+
message: turkish
|
|
514
819
|
? input.externalTaskId +
|
|
515
820
|
' teslim edilmeyecek. Bu klasör ne olsun?' +
|
|
516
821
|
(folder.clean
|
|
@@ -525,8 +830,33 @@ export function registerWorktreeTools(server, service) {
|
|
|
525
830
|
: folder.managed
|
|
526
831
|
? ' It has uncommitted changes; saving keeps them in a Git ref they can be restored from.'
|
|
527
832
|
: ' It has uncommitted changes; this is your own folder, so its files are never touched when it is freed.'),
|
|
528
|
-
options: folder.clean || !folder.managed
|
|
529
|
-
|
|
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)]);
|
|
530
860
|
const decision = await service.questionnaireResume({
|
|
531
861
|
repoRoot: input.repoRoot,
|
|
532
862
|
questionnaireId,
|