engineering-memory 1.11.22 → 1.11.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { ignoredRuntimeFiles, localRelativePath } from './worktree-preparation.js';
2
2
  import { decisionModeSchema } from './decision-mode-store.js';
3
- import { delegatedReasonSchema } from './questionnaire-store.js';
3
+ import { answerSourceName, delegatedReasonSchema } from './questionnaire-store.js';
4
4
  import { unreadableWorktree, } from './worktree-pool.js';
5
5
  import { validWorktreePolicy } from './worktree-policy.js';
6
6
  import { resolveTaskBase } from './branch-preferences.js';
@@ -1689,10 +1689,31 @@ export class BridgeService {
1689
1689
  catch (error) {
1690
1690
  throw refuse(error instanceof Error ? error.message : 'The resulting commit must be inspected.', 'memory.sync_start');
1691
1691
  }
1692
+ if (!proof)
1693
+ throw refuse('This task has nothing to publish: its project had no inspected source snapshot when the task was verified.', 'memory.sync_start');
1692
1694
  const response = await this.dependencies.client.request('/memory/sources/publish-task', { method: 'POST', body: proof });
1693
1695
  return response.data;
1694
1696
  });
1695
1697
  }
1698
+ async publishDelivered(repoRoot, taskId) {
1699
+ try {
1700
+ const proof = await this.dependencies.gate.publicationProof(repoRoot, taskId);
1701
+ if (!proof)
1702
+ return null;
1703
+ const response = await this.dependencies.client.request('/memory/sources/publish-task', { method: 'POST', body: proof });
1704
+ const answer = objectValue(response.data);
1705
+ return answer?.published === false
1706
+ ? { published: false, reason: String(answer.reason) }
1707
+ : { published: true };
1708
+ }
1709
+ catch (error) {
1710
+ return {
1711
+ published: false,
1712
+ reason: error instanceof Error ? error.message : String(error),
1713
+ nextAction: 'The delivery is recorded; only the source memory of this task was not published. The member who opened the task can call memory.publish_task in this folder before another task reuses it; otherwise memory.sync_start inspects the result.',
1714
+ };
1715
+ }
1716
+ }
1696
1717
  async onboardingRequest(operation, body) {
1697
1718
  return this.execute(async () => {
1698
1719
  if (operation === 'next' || operation === 'submit') {
@@ -2268,11 +2289,7 @@ export class BridgeService {
2268
2289
  questionnaireId,
2269
2290
  reason: waiver.reason,
2270
2291
  paths,
2271
- answerSource: question.answerSource?.kind === 'delegated_agent'
2272
- ? 'delegated_agent'
2273
- : question.answerSource?.kind === 'host_native_relay'
2274
- ? question.answerSource.hostTool
2275
- : 'mcp_form',
2292
+ answerSource: answerSourceName(question.answerSource),
2276
2293
  ...(delegatedEvidence(question)
2277
2294
  ? { delegatedDecision: delegatedEvidence(question) }
2278
2295
  : {}),
@@ -2545,9 +2562,44 @@ export class BridgeService {
2545
2562
  currentCommit: repository.git.head,
2546
2563
  folder,
2547
2564
  suggestedName,
2565
+ ...(input.workItemId
2566
+ ? await this.workItemBranches(repository.repoRoot, repository.projectId, input.workItemProjectId ?? repository.projectId, input.workItemId, preferences.data)
2567
+ : { planBranch: null, continueBranches: [] }),
2548
2568
  });
2549
2569
  });
2550
2570
  }
2571
+ async workItemBranches(repoRoot, projectId, itemProjectId, workItemId, preferences) {
2572
+ const git = this.dependencies.repositories.git;
2573
+ const [plan, runs, reserved, openDeliveries] = await Promise.all([
2574
+ this.dependencies.client.request(endpoints.workItemPlan(itemProjectId, workItemId)),
2575
+ this.dependencies.client.request(`${endpoints.workItemRuns(itemProjectId, workItemId)}?offset=0&limit=50`),
2576
+ git.protectedBranchNames(repoRoot),
2577
+ this.dependencies.client
2578
+ .request(`${endpoints.projectDeliveries(projectId)}?state=open&limit=100`)
2579
+ .then((response) => response.data?.items ?? [], () => []),
2580
+ ]);
2581
+ for (const preference of Object.values(preferences.branches))
2582
+ if (preference.branch)
2583
+ reserved.add(preference.branch);
2584
+ const used = [
2585
+ ...new Set((runs.data?.items ?? [])
2586
+ .filter((run) => run.projectId === projectId && run.mode !== 'read_only')
2587
+ .map((run) => run.branch)
2588
+ .filter((branch) => !!branch && !reserved.has(branch))),
2589
+ ];
2590
+ const planBranch = plan.data?.confirmed ? plan.data.branch : null;
2591
+ const continueBranches = !planBranch
2592
+ ? used.slice(0, 2)
2593
+ : used.includes(planBranch) || (await git.branchExists(repoRoot, planBranch))
2594
+ ? [planBranch]
2595
+ : [];
2596
+ const open = new Set(openDeliveries.map((delivery) => delivery.taskId));
2597
+ return {
2598
+ planBranch,
2599
+ continueBranches,
2600
+ openDeliveryBranches: continueBranches.filter((branch) => (runs.data?.items ?? []).some((run) => run.branch === branch && open.has(run.id))),
2601
+ };
2602
+ }
2551
2603
  async suggestedBranchName(repoRoot, externalTaskId) {
2552
2604
  const candidates = suggestedBranchNames(externalTaskId);
2553
2605
  for (const candidate of candidates)
@@ -2773,8 +2825,17 @@ export class BridgeService {
2773
2825
  }
2774
2826
  if (operation === 'release') {
2775
2827
  await this.requireDeliveredTaskWork(entry);
2828
+ const sourcePublication = outcome === 'delivered' && entry.taskId && entry.pendingDelivery
2829
+ ? await this.publishDelivered(repository.repoRoot, entry.taskId)
2830
+ : null;
2776
2831
  await pool.release(projectId, repository.repoRoot, input.generation, outcome === 'delivered' ? outcome : undefined);
2777
2832
  this.poolAllocations.delete(repository.repoRoot);
2833
+ return asJsonValue({
2834
+ operation,
2835
+ complete: true,
2836
+ repoRoot: repository.repoRoot,
2837
+ ...(sourcePublication ? { sourcePublication } : {}),
2838
+ });
2778
2839
  }
2779
2840
  if (operation === 'cancel_delivery') {
2780
2841
  if (!entry.pendingDelivery)
@@ -2882,7 +2943,8 @@ export class BridgeService {
2882
2943
  throw refuse(`This folder is held by task ${holder.externalTaskId}. Call task.branch again: its start form asks whether ${holder.externalTaskId} should move out of this folder before the new branch starts here, or opens the new task in a separate worktree.`, 'task.branch');
2883
2944
  if (repository.git.changedPaths.length > 0)
2884
2945
  throw refuse(`This folder has uncommitted changes, so the new branch cannot start here and ${holder.externalTaskId} stays in it. Call task.branch with a new decisionAttempt and open the task in a separate worktree.`, 'task.branch');
2885
- if (await git.branchExists(repository.repoRoot, input.name))
2946
+ if (input.base.kind !== 'existing' &&
2947
+ (await git.branchExists(repository.repoRoot, input.name)))
2886
2948
  throw refuse(`Branch ${input.name} already exists, so it cannot start here and ${holder.externalTaskId} stays in this folder. Call task.branch with a new decisionAttempt and choose a new branch name.`, 'task.branch');
2887
2949
  if (holder.generation) {
2888
2950
  await pool.moveOut(projectId, repository.repoRoot, holder.generation, holder.oid ?? undefined);
@@ -2914,6 +2976,7 @@ export class BridgeService {
2914
2976
  keepCurrent: input.keepCurrent,
2915
2977
  inPlace: !existing && input.inPlace,
2916
2978
  resume: Boolean(existing),
2979
+ continued: !existing && input.base?.kind === 'existing',
2917
2980
  }, allocationPolicy));
2918
2981
  allocationTimer.mark('pool_allocate');
2919
2982
  allocationTimer.finish();
@@ -3725,6 +3788,22 @@ export class BridgeService {
3725
3788
  return asJsonValue(response.data);
3726
3789
  });
3727
3790
  }
3791
+ async workItemComment(input) {
3792
+ return this.execute(async () => {
3793
+ const response = await this.dependencies.client.request(endpoints.workItemComments(input.projectId, input.workItemId), { method: 'POST', body: cleanJson(input.data) });
3794
+ return asJsonValue(response.data);
3795
+ });
3796
+ }
3797
+ async workItemComments(input) {
3798
+ return this.execute(async () => {
3799
+ const query = new URLSearchParams({
3800
+ offset: String(input.offset ?? 0),
3801
+ limit: String(input.limit ?? 50),
3802
+ });
3803
+ const response = await this.dependencies.client.request(`${endpoints.workItemComments(input.projectId, input.workItemId)}?${query}`);
3804
+ return asJsonValue(response.data);
3805
+ });
3806
+ }
3728
3807
  async workItemPlan(input) {
3729
3808
  return this.execute(async () => {
3730
3809
  const response = await this.dependencies.client.request(endpoints.workItemPlan(input.projectId, input.workItemId));
@@ -4023,7 +4102,7 @@ export class BridgeService {
4023
4102
  const livePointers = authenticated && state === 'bound'
4024
4103
  ? this.dependencies.activeContexts.list(repository.repoFingerprint)
4025
4104
  : Promise.resolve([]);
4026
- const [client, liveTasksRaw, workItems, gitPreferences, worktrees, pendingQuestionnaires, moved,] = await Promise.all([
4105
+ const [client, liveTasksRaw, workItems, gitPreferences, worktrees, pendingQuestionnaires, moved, deliveries,] = await Promise.all([
4027
4106
  this.clientUpdate(authenticated),
4028
4107
  livePointers,
4029
4108
  authenticated && state === 'bound' && projectId
@@ -4044,6 +4123,9 @@ export class BridgeService {
4044
4123
  authenticated && state === 'bound' && projectId
4045
4124
  ? this.movedAway(repository, projectId)
4046
4125
  : Promise.resolve({}),
4126
+ authenticated && state === 'bound' && projectId
4127
+ ? this.openDeliveries(projectId)
4128
+ : Promise.resolve(null),
4047
4129
  ]);
4048
4130
  timer.mark('independent_lookups');
4049
4131
  const liveTasks = liveTasksRaw.map(describePointer);
@@ -4083,6 +4165,7 @@ export class BridgeService {
4083
4165
  ...(pendingQuestionnaires.deferred.length > 0
4084
4166
  ? { deferredTaskStarts: pendingQuestionnaires.deferred }
4085
4167
  : {}),
4168
+ ...(deliveries ? { openDeliveries: deliveries } : {}),
4086
4169
  client,
4087
4170
  ...moved,
4088
4171
  ...(incomplete && state === 'bound'
@@ -4126,6 +4209,30 @@ export class BridgeService {
4126
4209
  return {};
4127
4210
  }
4128
4211
  }
4212
+ async openDeliveries(projectId) {
4213
+ try {
4214
+ const response = await this.dependencies.client.request(`${endpoints.projectDeliveries(projectId)}?state=open&limit=5`);
4215
+ const page = objectValue(response.data);
4216
+ const items = Array.isArray(page?.items) ? page.items : [];
4217
+ if (!items.length)
4218
+ return null;
4219
+ const pool = this.dependencies.worktreePool;
4220
+ return {
4221
+ total: page.total ?? items.length,
4222
+ items: await Promise.all(items.map(async (item) => {
4223
+ const folder = await pool?.forTask(String(objectValue(item)?.taskId));
4224
+ return {
4225
+ ...objectOrEmpty(item),
4226
+ folderHere: folder?.pendingDelivery ? folder.repoRoot : null,
4227
+ };
4228
+ })),
4229
+ nextAction: 'These closed tasks have no concluded delivery. Mention them to the user in one line. For one whose folderHere is set, task.close with its taskId in that folder asks and performs the Git delivery there. Any other can be answered or concluded with task.delivery when the user asks.',
4230
+ };
4231
+ }
4232
+ catch {
4233
+ return null;
4234
+ }
4235
+ }
4129
4236
  async actionableWorkItems(projectId) {
4130
4237
  try {
4131
4238
  const response = await this.dependencies.client.request(`${endpoints.workItemList(projectId)}?limit=100&actionable=true`);
@@ -4431,6 +4538,44 @@ export class BridgeService {
4431
4538
  return asJsonValue({ queued: true, outboxId: queued.id, idempotencyKey });
4432
4539
  }
4433
4540
  }
4541
+ async taskDeliveryAnswer(input) {
4542
+ return this.execute(() => this.mutateWithOutbox('task.delivery', endpoints.taskDeliveryAnswer(input.projectId, input.taskId), cleanJson({
4543
+ expectedVersion: input.expectedVersion,
4544
+ choice: input.choice,
4545
+ baseBranch: input.baseBranch,
4546
+ answerSource: input.answerSource,
4547
+ })));
4548
+ }
4549
+ async taskDeliveryCancel(input) {
4550
+ return this.execute(() => this.mutateWithOutbox('task.delivery', endpoints.taskDeliveryCancel(input.projectId, input.taskId), { reason: input.reason }));
4551
+ }
4552
+ async taskDeliveryRecord(input) {
4553
+ return this.execute(async () => {
4554
+ const response = await this.dependencies.client.request(endpoints.taskDelivery(input.projectId, input.taskId));
4555
+ const folder = await this.dependencies.worktreePool?.forTask(input.taskId);
4556
+ return asJsonValue({
4557
+ record: response.data,
4558
+ folder: folder?.pendingDelivery ? folder.repoRoot : null,
4559
+ });
4560
+ });
4561
+ }
4562
+ async reportDeliveryOutcome(report) {
4563
+ const path = report.outcome === 'delivered'
4564
+ ? endpoints.taskDeliveryDeliver(report.projectId, report.taskId)
4565
+ : endpoints.taskDeliveryCancel(report.projectId, report.taskId);
4566
+ const body = report.outcome === 'delivered'
4567
+ ? { commit: report.commit, pushed: report.pushed }
4568
+ : { reason: report.reason };
4569
+ await this.dependencies.outbox.enqueue({
4570
+ operation: 'task.delivery',
4571
+ method: 'POST',
4572
+ path,
4573
+ body,
4574
+ projectId: report.projectId,
4575
+ idempotencyKey: sha256(stableStringify({ path, body })),
4576
+ });
4577
+ this.deliverInBackground();
4578
+ }
4434
4579
  async enqueueTaskReceipt(operation, path, taskId, repoRoot, content) {
4435
4580
  const repository = await this.dependencies.repositories.resolve(repoRoot);
4436
4581
  return this.taskExclusive(taskId, async () => {
@@ -4479,7 +4624,11 @@ export class BridgeService {
4479
4624
  for (const entry of entries) {
4480
4625
  const owner = taskIdFromDeliverySafe(entry);
4481
4626
  const body = objectOrEmpty(entry.body);
4482
- const key = owner ? 'task:' + owner : 'project:' + String(body.projectId ?? 'unscoped');
4627
+ const key = owner
4628
+ ? 'task:' + owner
4629
+ : entry.operation === 'task.delivery'
4630
+ ? 'delivery:' + entry.path.slice(0, entry.path.lastIndexOf('/'))
4631
+ : 'project:' + String(body.projectId ?? 'unscoped');
4483
4632
  const group = groups.get(key) ?? [];
4484
4633
  group.push(entry);
4485
4634
  groups.set(key, group);
@@ -4505,6 +4654,12 @@ export class BridgeService {
4505
4654
  synchronized.push(entry.id);
4506
4655
  }
4507
4656
  catch (error) {
4657
+ if (entry.operation === 'task.delivery' &&
4658
+ error instanceof ApiResponseError &&
4659
+ (error.httpStatus === 404 || error.httpStatus === 409)) {
4660
+ await this.dependencies.outbox.acknowledge(entry.id);
4661
+ continue;
4662
+ }
4508
4663
  const errorKind = classifyError(error);
4509
4664
  await this.dependencies.outbox.markAttempt(entry.id, errorKind);
4510
4665
  if (entry.journalRef)
@@ -4874,6 +5029,9 @@ export class BridgeService {
4874
5029
  response = await this.dependencies.client.request(endpoints.taskClose, {
4875
5030
  method: 'POST',
4876
5031
  body,
5032
+ ...(this.dependencies.clientVersion
5033
+ ? { headers: { 'x-client-version': this.dependencies.clientVersion } }
5034
+ : {}),
4877
5035
  });
4878
5036
  }
4879
5037
  catch (error) {
@@ -5371,7 +5529,7 @@ function deliveryQuestion(touchedContract, destination, publishApplicable) {
5371
5529
  : {}),
5372
5530
  afterPullRequest: 'Do not end the turn once a pull request exists. Check whether it merges cleanly, report the conflicting files if it does not, and ask whether to resolve them before touching anything.',
5373
5531
  afterCommit: publishApplicable
5374
- ? 'Call memory.publish_task with this closed task id and repoRoot after the authorized commit is created.'
5532
+ ? "After the authorized delivery, call worktree.release in this folder with deliveryOutcome delivered: it publishes this task's source memory and then records the delivery for the project. Its sourcePublication says whether publication happened; call memory.publish_task only when it names a reason you can fix. A task without a worktree to release calls memory.publish_task after the commit instead."
5375
5533
  : 'This task has nothing to publish (see sourcePublication on the close response); do not call memory.publish_task.',
5376
5534
  });
5377
5535
  }
@@ -49,6 +49,7 @@ export function createBridgeService(options = {}) {
49
49
  const repositoryDecisions = new RepositoryDecisionStore(stateRoot);
50
50
  const updateChoices = new UpdateChoiceStore(stateRoot);
51
51
  const shadowNotices = new ShadowNoticeStore(stateRoot);
52
+ let service;
52
53
  const worktreePool = new WorktreePool(stateRoot, {
53
54
  ...(options.ownerId ? { ownerId: options.ownerId } : {}),
54
55
  pendingWork: async (entry) => {
@@ -59,11 +60,12 @@ export function createBridgeService(options = {}) {
59
60
  pointer?.closeIntent ||
60
61
  (await outbox.listForTask(entry.taskId)).length);
61
62
  },
63
+ onDeliveryOutcome: async (report) => service?.reportDeliveryOutcome(report),
62
64
  });
63
65
  const gate = new VerificationGate(stateRoot, git, outbox, undefined, worktreePool);
64
66
  const principalState = new PrincipalStateGuard(stateRoot, credentials, cache, outbox, activeContexts, gate);
65
67
  const languages = new LanguageStore(stateRoot);
66
- return new BridgeService({
68
+ return (service = new BridgeService({
67
69
  releaseNotes: new ReleaseNotes(stateRoot, client, credentials),
68
70
  worktreePool,
69
71
  worktreePolicy: new WorktreePolicyCache(stateRoot),
@@ -83,6 +85,6 @@ export function createBridgeService(options = {}) {
83
85
  onboarding: new OnboardingStore(stateRoot),
84
86
  clientVersion: options.clientVersion === undefined ? config.clientVersion : options.clientVersion,
85
87
  principalState,
86
- });
88
+ }));
87
89
  }
88
90
  //# sourceMappingURL=create-bridge-service.js.map
@@ -170,6 +170,13 @@ const answerSourceSchema = z.union([
170
170
  impact: z.enum(['routine', 'critical']),
171
171
  }),
172
172
  ]);
173
+ export function answerSourceName(source) {
174
+ return source?.kind === 'delegated_agent'
175
+ ? 'delegated_agent'
176
+ : source?.kind === 'host_native_relay'
177
+ ? source.hostTool
178
+ : 'mcp_form';
179
+ }
173
180
  const storedAnswerSchema = z.strictObject({
174
181
  requestKey: z.string().regex(/^questionnaire_[0-9a-f]{64}$/),
175
182
  answeredAt: z.string().datetime(),
@@ -18,6 +18,7 @@ const startCopy = {
18
18
  fetched: (role, source) => `The project's ${role} branch ${source} is fetched from the remote and the new branch starts from that exact commit.`,
19
19
  pinned: (source) => `The new branch starts where this folder is right now, ${source}. Nothing is fetched.`,
20
20
  typedBranch: 'Write a branch name on origin below. It is fetched and the new branch starts from it.',
21
+ continued: (branch) => `No new branch is created. The work continues from the newest commit of ${branch}, fetched from origin when this clone lacks it.`,
21
22
  suggested: 'The branch is created with this name.',
22
23
  typedName: 'Write the name below. A name that already exists is refused and the form is asked again.',
23
24
  },
@@ -37,6 +38,7 @@ const startCopy = {
37
38
  fetched: (role, source) => `Projenin ${role} dalı ${source} uzak sunucudan çekilir ve yeni dal tam o commit’ten başlar.`,
38
39
  pinned: (source) => `Yeni dal, bu klasörün şu an üzerinde olduğu ${source} noktasından başlar. Hiçbir şey çekilmez.`,
39
40
  typedBranch: 'origin üzerindeki dal adını aşağıya yaz. O dal çekilir ve yeni dal ondan başlar.',
41
+ continued: (branch) => `Yeni dal açılmaz. İş ${branch} dalının en yeni commit’inden sürer; bu klonda yoksa origin’den çekilir.`,
40
42
  suggested: 'Dal bu adla açılır.',
41
43
  typedName: 'Adı aşağıya yaz. Zaten var olan bir ad kabul edilmez, form yeniden sorulur.',
42
44
  },
@@ -47,6 +49,8 @@ const startWording = z.strictObject({
47
49
  message: text,
48
50
  keepContext: text,
49
51
  newBranch: text,
52
+ newBranchOrContinue: text,
53
+ continuePlan: text,
50
54
  example: text,
51
55
  stay: text,
52
56
  base: z.strictObject({
@@ -56,6 +60,9 @@ const startWording = z.strictObject({
56
60
  other: text,
57
61
  otherDetail: text,
58
62
  fetchDetail: text,
63
+ continue: text,
64
+ continueDetail: text,
65
+ continueOpenDetail: text,
59
66
  field: text,
60
67
  }),
61
68
  location: z.strictObject({
@@ -92,6 +99,8 @@ export function sourceLabel(base, currentBranch) {
92
99
  return base.remote + '/' + base.branch;
93
100
  if (base.kind === 'local')
94
101
  return base.ref;
102
+ if (base.kind === 'existing')
103
+ return base.branch;
95
104
  if (base.kind === 'current')
96
105
  return (currentBranch ?? 'HEAD') + (base.commit ? ' (' + base.commit.slice(0, 7) + ')' : '');
97
106
  return base.kind;
@@ -135,14 +144,20 @@ export function taskStartDefinition(facts) {
135
144
  ...option,
136
145
  label: base?.kind === 'remote'
137
146
  ? sourceLabel(base, facts.currentBranch)
147
+ : base?.kind === 'existing'
148
+ ? format(copy.base.continue, language, { branch: base.branch })
149
+ : option.id === 'current'
150
+ ? format(copy.base.local, language, { branch })
151
+ : copy.base.other,
152
+ description: base?.kind === 'existing'
153
+ ? facts.openDeliveryBranches?.includes(base.branch)
154
+ ? copy.base.continueOpenDetail
155
+ : copy.base.continueDetail
138
156
  : option.id === 'current'
139
- ? format(copy.base.local, language, { branch })
140
- : copy.base.other,
141
- description: option.id === 'current'
142
- ? copy.base.localDetail
143
- : option.id === 'other'
144
- ? copy.base.otherDetail
145
- : copy.base.fetchDetail,
157
+ ? copy.base.localDetail
158
+ : option.id === 'other'
159
+ ? copy.base.otherDetail
160
+ : copy.base.fetchDetail,
146
161
  };
147
162
  }),
148
163
  textField: { ...question.textField, title: copy.base.field },
@@ -184,8 +199,11 @@ export function taskStartDefinition(facts) {
184
199
  message: copy.message,
185
200
  context: keepOnly
186
201
  ? copy.keepContext
187
- : format(copy.newBranch, language, { name: binding.name }) +
188
- (binding.base ? ' · ' + sourceLabel(binding.base, facts.currentBranch) : ''),
202
+ : binding.base?.kind === 'existing'
203
+ ? format(copy.continuePlan, language, { branch: binding.base.branch })
204
+ : format(Object.values(binding.bases ?? {}).some((base) => base.kind === 'existing')
205
+ ? copy.newBranchOrContinue
206
+ : copy.newBranch, language, { name: binding.name }) + (binding.base ? ' · ' + sourceLabel(binding.base, facts.currentBranch) : ''),
189
207
  example: keepOnly ? format(copy.stay, language, { source: current }) : copy.example,
190
208
  questions,
191
209
  binding: JSON.parse(JSON.stringify(binding)),
@@ -269,17 +287,27 @@ function legacyTaskStartDefinition(facts) {
269
287
  label: base.remote + '/' + base.branch + ' (' + role + ')',
270
288
  description: copy.fetched(copy.roles[role], base.remote + '/' + base.branch),
271
289
  });
290
+ const continued = facts.continueBranches ?? [];
272
291
  binding.bases = {
273
292
  current: {
274
293
  kind: 'current',
275
294
  ...(facts.currentCommit ? { commit: facts.currentCommit } : {}),
276
295
  },
277
296
  ...Object.fromEntries(roles.map(({ role, base }) => [role, base])),
297
+ ...Object.fromEntries(continued.map((branch, index) => [
298
+ 'continue_' + (index + 1),
299
+ { kind: 'existing', branch },
300
+ ])),
278
301
  };
279
302
  questions.push({
280
303
  id: 'base',
281
304
  message: tr ? 'Hangi daldan başlasın?' : 'Which branch should it start from?',
282
305
  options: [
306
+ ...continued.map((branch, index) => ({
307
+ id: 'continue_' + (index + 1),
308
+ label: (tr ? 'Var olan dalı sürdür: ' : 'Continue the existing branch ') + branch,
309
+ description: copy.continued(branch),
310
+ })),
283
311
  ...roles.slice(0, 1).map(roleOption),
284
312
  {
285
313
  id: 'current',
@@ -422,12 +450,14 @@ export function taskStartChoice(record) {
422
450
  ? binding.bases?.[baseAnswer.choice]
423
451
  : undefined);
424
452
  const nameAnswer = record.answers.name;
425
- const name = binding.name ??
426
- (nameAnswer?.choice === 'custom'
427
- ? nameAnswer.text
428
- : nameAnswer?.choice === 'suggested'
429
- ? binding.suggestedName
430
- : undefined);
453
+ const name = base?.kind === 'existing'
454
+ ? base.branch
455
+ : (binding.name ??
456
+ (nameAnswer?.choice === 'custom'
457
+ ? nameAnswer.text
458
+ : nameAnswer?.choice === 'suggested'
459
+ ? binding.suggestedName
460
+ : undefined));
431
461
  if (!base || !name)
432
462
  return null;
433
463
  return {