taskplane 0.29.2 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/bin/gitignore-patterns.mjs +11 -8
  2. package/bin/rpc-wrapper.mjs +410 -357
  3. package/bin/taskplane.mjs +533 -250
  4. package/extensions/reviewer-extension.ts +17 -11
  5. package/extensions/taskplane/abort.ts +50 -18
  6. package/extensions/taskplane/agent-bridge-extension.ts +232 -105
  7. package/extensions/taskplane/agent-host.ts +224 -97
  8. package/extensions/taskplane/cleanup.ts +71 -42
  9. package/extensions/taskplane/config-loader.ts +142 -58
  10. package/extensions/taskplane/config-schema.ts +6 -13
  11. package/extensions/taskplane/config.ts +10 -2
  12. package/extensions/taskplane/diagnostic-reports.ts +59 -47
  13. package/extensions/taskplane/diagnostics.ts +13 -13
  14. package/extensions/taskplane/discovery.ts +35 -61
  15. package/extensions/taskplane/engine-worker.ts +53 -46
  16. package/extensions/taskplane/engine.ts +1760 -602
  17. package/extensions/taskplane/execution.ts +426 -206
  18. package/extensions/taskplane/extension.ts +1073 -598
  19. package/extensions/taskplane/formatting.ts +136 -124
  20. package/extensions/taskplane/git.ts +0 -2
  21. package/extensions/taskplane/lane-runner.ts +542 -311
  22. package/extensions/taskplane/mailbox.ts +57 -49
  23. package/extensions/taskplane/merge.ts +662 -383
  24. package/extensions/taskplane/messages.ts +109 -51
  25. package/extensions/taskplane/migrations.ts +1 -1
  26. package/extensions/taskplane/path-resolver.ts +8 -9
  27. package/extensions/taskplane/persistence.ts +425 -262
  28. package/extensions/taskplane/process-registry.ts +36 -7
  29. package/extensions/taskplane/quality-gate.ts +107 -55
  30. package/extensions/taskplane/resume.ts +774 -267
  31. package/extensions/taskplane/sessions.ts +1 -1
  32. package/extensions/taskplane/settings-tui.ts +505 -164
  33. package/extensions/taskplane/sidecar-telemetry.ts +25 -10
  34. package/extensions/taskplane/supervisor.ts +477 -270
  35. package/extensions/taskplane/task-executor-core.ts +178 -53
  36. package/extensions/taskplane/types.ts +186 -108
  37. package/extensions/taskplane/verification.ts +27 -22
  38. package/extensions/taskplane/waves.ts +59 -43
  39. package/extensions/taskplane/workspace.ts +14 -12
  40. package/extensions/taskplane/worktree.ts +218 -196
  41. package/package.json +14 -2
@@ -6,7 +6,16 @@ import { join } from "path";
6
6
  import { truncateToWidth } from "@mariozechner/pi-tui";
7
7
 
8
8
  import { parseDependencyReference } from "./discovery.ts";
9
- import type { LaneAssignment, MonitorState, OrchBatchRuntimeState, OrchDashboardViewModel, OrchLaneCardData, OrchSummaryCounts, ParsedTask, WaveComputationResult } from "./types.ts";
9
+ import type {
10
+ LaneAssignment,
11
+ MonitorState,
12
+ OrchBatchRuntimeState,
13
+ OrchDashboardViewModel,
14
+ OrchLaneCardData,
15
+ OrchSummaryCounts,
16
+ ParsedTask,
17
+ WaveComputationResult,
18
+ } from "./types.ts";
10
19
  import { getTaskDurationMinutes, SIZE_DURATION_MINUTES } from "./types.ts";
11
20
 
12
21
  // ── Wave Output Formatting ───────────────────────────────────────────
@@ -30,9 +39,7 @@ export function formatDependencyGraph(
30
39
  const lines: string[] = [];
31
40
 
32
41
  // Sort tasks deterministically by ID
33
- const sortedTasks = [...pending.values()].sort((a, b) =>
34
- a.taskId.localeCompare(b.taskId),
35
- );
42
+ const sortedTasks = [...pending.values()].sort((a, b) => a.taskId.localeCompare(b.taskId));
36
43
 
37
44
  // Build downstream index: taskID → tasks that depend on it
38
45
  const downstream = new Map<string, string[]>();
@@ -130,14 +137,8 @@ export function formatDependencyGraph(
130
137
  const dependents = (downstream.get(target) || []).sort();
131
138
  if (dependents.length > 0) {
132
139
  hasDownstream = true;
133
- const status = completed.has(target)
134
- ? ""
135
- : pending.has(target)
136
- ? "⏳"
137
- : "❓";
138
- lines.push(
139
- ` ${target} ${status} ← ${dependents.join(", ")}`,
140
- );
140
+ const status = completed.has(target) ? "✅" : pending.has(target) ? "⏳" : "❓";
141
+ lines.push(` ${target} ${status} ← ${dependents.join(", ")}`);
141
142
  }
142
143
  }
143
144
  if (!hasDownstream) {
@@ -146,9 +147,7 @@ export function formatDependencyGraph(
146
147
 
147
148
  // Section 3: Independent tasks (no deps, nothing depends on them)
148
149
  const independentTasks = sortedTasks.filter(
149
- (t) =>
150
- t.dependencies.length === 0 &&
151
- !(downstream.get(t.taskId)?.length),
150
+ (t) => t.dependencies.length === 0 && !downstream.get(t.taskId)?.length,
152
151
  );
153
152
  if (independentTasks.length > 0) {
154
153
  lines.push("");
@@ -206,7 +205,7 @@ export function formatWavePlan(
206
205
 
207
206
  lines.push(
208
207
  `🌊 Execution Plan: ${result.waves.length} wave(s), ` +
209
- `${totalTasks} task(s), up to ${maxLanesUsed} lane(s)`,
208
+ `${totalTasks} task(s), up to ${maxLanesUsed} lane(s)`,
210
209
  );
211
210
  lines.push("");
212
211
 
@@ -225,46 +224,31 @@ export function formatWavePlan(
225
224
  const parallel = laneCount > 1 ? "parallel" : "serial";
226
225
 
227
226
  lines.push(
228
- ` Wave ${wave.waveNumber}: ${taskCount} task(s) across ` +
229
- `${laneCount} lane(s) [${parallel}]`,
227
+ ` Wave ${wave.waveNumber}: ${taskCount} task(s) across ` + `${laneCount} lane(s) [${parallel}]`,
230
228
  );
231
229
 
232
230
  // Calculate wave duration: critical path = max lane duration
233
231
  let maxLaneDuration = 0;
234
232
 
235
233
  // Sort lanes deterministically by lane number
236
- const sortedLanes = [...laneGroups.entries()].sort(
237
- (a, b) => a[0] - b[0],
238
- );
234
+ const sortedLanes = [...laneGroups.entries()].sort((a, b) => a[0] - b[0]);
239
235
 
240
236
  for (const [lane, assignments] of sortedLanes) {
241
237
  // Sort tasks within lane by task ID for deterministic output
242
- const sortedAssignments = [...assignments].sort((a, b) =>
243
- a.taskId.localeCompare(b.taskId),
244
- );
245
- const taskList = sortedAssignments
246
- .map((a) => `${a.taskId} [${a.task.size}]`)
247
- .join(", ");
238
+ const sortedAssignments = [...assignments].sort((a, b) => a.taskId.localeCompare(b.taskId));
239
+ const taskList = sortedAssignments.map((a) => `${a.taskId} [${a.task.size}]`).join(", ");
248
240
  const laneDuration = sortedAssignments.reduce(
249
- (sum, a) =>
250
- sum + getTaskDurationMinutes(a.task.size, sizeWeights),
241
+ (sum, a) => sum + getTaskDurationMinutes(a.task.size, sizeWeights),
251
242
  0,
252
243
  );
253
244
  if (laneDuration > maxLaneDuration) maxLaneDuration = laneDuration;
254
- const serialNote =
255
- sortedAssignments.length > 1 ? " (serial)" : "";
256
- lines.push(
257
- ` Lane ${lane}: ${taskList}${serialNote} ` +
258
- `[est. ${laneDuration} min]`,
259
- );
245
+ const serialNote = sortedAssignments.length > 1 ? " (serial)" : "";
246
+ lines.push(` Lane ${lane}: ${taskList}${serialNote} ` + `[est. ${laneDuration} min]`);
260
247
  }
261
248
 
262
249
  // Critical path for this wave
263
250
  totalEstimate += maxLaneDuration;
264
- lines.push(
265
- ` ⏱ Wave duration: ${maxLaneDuration} min ` +
266
- `(critical path: longest lane)`,
267
- );
251
+ lines.push(` ⏱ Wave duration: ${maxLaneDuration} min ` + `(critical path: longest lane)`);
268
252
  lines.push("");
269
253
  }
270
254
 
@@ -273,17 +257,15 @@ export function formatWavePlan(
273
257
  lines.push(`📊 Total estimated duration: ${totalEstimate} min (~${totalHours} hours)`);
274
258
  lines.push(
275
259
  ` Duration model: S=${SIZE_DURATION_MINUTES["S"]}m, ` +
276
- `M=${SIZE_DURATION_MINUTES["M"]}m, L=${SIZE_DURATION_MINUTES["L"]}m`,
260
+ `M=${SIZE_DURATION_MINUTES["M"]}m, L=${SIZE_DURATION_MINUTES["L"]}m`,
277
261
  );
278
262
  lines.push(
279
- " Critical path: sum of per-wave bottleneck lanes " +
280
- "(waves sequential, lanes parallel)",
263
+ " Critical path: sum of per-wave bottleneck lanes " + "(waves sequential, lanes parallel)",
281
264
  );
282
265
 
283
266
  return lines.join("\n");
284
267
  }
285
268
 
286
-
287
269
  // ── Summary Helpers ──────────────────────────────────────────────────
288
270
 
289
271
  /**
@@ -315,7 +297,10 @@ export function computeOrchSummaryCounts(
315
297
  const failed = batchState.failedTasks;
316
298
  const blocked = batchState.blockedTasks;
317
299
  const total = batchState.totalTasks;
318
- const queued = Math.max(0, total - completed - failed - blocked - stalled - running - batchState.skippedTasks);
300
+ const queued = Math.max(
301
+ 0,
302
+ total - completed - failed - blocked - stalled - running - batchState.skippedTasks,
303
+ );
319
304
 
320
305
  return { completed, running, queued, failed, blocked, stalled, total };
321
306
  }
@@ -360,9 +345,10 @@ export function buildDashboardViewModel(
360
345
  const summary = computeOrchSummaryCounts(batchState, monitorState);
361
346
  const elapsed = formatElapsedTime(batchState.startedAt, batchState.endedAt);
362
347
 
363
- const waveProgress = batchState.totalWaves > 0
364
- ? `${Math.max(0, batchState.currentWaveIndex + 1)}/${batchState.totalWaves}`
365
- : "0/0";
348
+ const waveProgress =
349
+ batchState.totalWaves > 0
350
+ ? `${Math.max(0, batchState.currentWaveIndex + 1)}/${batchState.totalWaves}`
351
+ : "0/0";
366
352
 
367
353
  // Build lane cards from monitor state (if available) or current lanes
368
354
  const laneCards: OrchLaneCardData[] = [];
@@ -372,15 +358,16 @@ export function buildDashboardViewModel(
372
358
  // lanes, but monitorState may still hold wave N's data until the first
373
359
  // poll of wave N+1's monitor. Detect this mismatch by checking whether
374
360
  // the monitor's lane numbers match the current allocation.
375
- const monitorIsFresh = monitorState && monitorState.lanes.length > 0 && (
361
+ const monitorIsFresh =
362
+ monitorState &&
363
+ monitorState.lanes.length > 0 &&
376
364
  // If no current allocation, monitor data is the best we have
377
365
  // (covers terminal phases like completed/failed/stopped)
378
- batchState.currentLanes.length === 0 ||
379
- // If allocated lanes exist, verify monitor lanes match them
380
- monitorState.lanes.some(ml =>
381
- batchState.currentLanes.some(cl => cl.laneNumber === ml.laneNumber),
382
- )
383
- );
366
+ (batchState.currentLanes.length === 0 ||
367
+ // If allocated lanes exist, verify monitor lanes match them
368
+ monitorState.lanes.some((ml) =>
369
+ batchState.currentLanes.some((cl) => cl.laneNumber === ml.laneNumber),
370
+ ));
384
371
 
385
372
  // TP-170: Build a laneNumber → AllocatedLane index for identity reconciliation.
386
373
  // In workspace mode, the monitor’s sessionName (e.g., "orch-henry-api-lane-1")
@@ -438,7 +425,11 @@ export function buildDashboardViewModel(
438
425
  totalChecked: snap?.totalChecked || 0,
439
426
  totalItems: snap?.totalItems || 0,
440
427
  completedTasks: lane.completedTasks.length,
441
- totalLaneTasks: lane.completedTasks.length + lane.failedTasks.length + lane.remainingTasks.length + (lane.currentTaskId ? 1 : 0),
428
+ totalLaneTasks:
429
+ lane.completedTasks.length +
430
+ lane.failedTasks.length +
431
+ lane.remainingTasks.length +
432
+ (lane.currentTaskId ? 1 : 0),
442
433
  status,
443
434
  stallReason: snap?.stallReason || null,
444
435
  });
@@ -468,7 +459,7 @@ export function buildDashboardViewModel(
468
459
 
469
460
  // Determine attach hint
470
461
  let attachHint = "";
471
- const aliveLane = laneCards.find(l => l.sessionAlive && l.status === "running");
462
+ const aliveLane = laneCards.find((l) => l.sessionAlive && l.status === "running");
472
463
  if (aliveLane) {
473
464
  attachHint = `Use /orch-sessions to inspect active lane sessions (${aliveLane.sessionName})`;
474
465
  } else if (laneCards.length > 0) {
@@ -513,19 +504,29 @@ export function buildDashboardViewModel(
513
504
  */
514
505
  export function renderLaneCard(card: OrchLaneCardData, colWidth: number, theme: any): string[] {
515
506
  const w = colWidth - 2; // inner width (excluding │ borders)
516
- const trunc = (s: string, max: number) => s.length > max ? s.slice(0, max - 3) + "..." : s;
507
+ const trunc = (s: string, max: number) => (s.length > max ? s.slice(0, max - 3) + "..." : s);
517
508
 
518
509
  // Status icon and color
519
- const statusIcon = card.status === "succeeded" ? "✓"
520
- : card.status === "running" ? "●"
521
- : card.status === "failed" ? ""
522
- : card.status === "stalled" ? "⚠"
523
- : "";
524
- const statusColor = card.status === "succeeded" ? "success"
525
- : card.status === "running" ? "accent"
526
- : card.status === "failed" ? "error"
527
- : card.status === "stalled" ? "warning"
528
- : "dim";
510
+ const statusIcon =
511
+ card.status === "succeeded"
512
+ ? ""
513
+ : card.status === "running"
514
+ ? ""
515
+ : card.status === "failed"
516
+ ? ""
517
+ : card.status === "stalled"
518
+ ? ""
519
+ : "";
520
+ const statusColor =
521
+ card.status === "succeeded"
522
+ ? "success"
523
+ : card.status === "running"
524
+ ? "accent"
525
+ : card.status === "failed"
526
+ ? "error"
527
+ : card.status === "stalled"
528
+ ? "warning"
529
+ : "dim";
529
530
 
530
531
  // Line 1: Session name (e.g., "⎡orch-lane-1⎤")
531
532
  const sessionLabel = `⎡${card.sessionName}⎤`;
@@ -535,9 +536,11 @@ export function renderLaneCard(card: OrchLaneCardData, colWidth: number, theme:
535
536
  // Line 2: Status + current task
536
537
  const taskInfo = card.currentTaskId
537
538
  ? `${statusIcon} ${card.currentTaskId}`
538
- : card.status === "succeeded" ? `${statusIcon} done`
539
- : card.status === "failed" ? `${statusIcon} failed`
540
- : `${statusIcon} idle`;
539
+ : card.status === "succeeded"
540
+ ? `${statusIcon} done`
541
+ : card.status === "failed"
542
+ ? `${statusIcon} failed`
543
+ : `${statusIcon} idle`;
541
544
  const taskStr = theme.fg(statusColor, trunc(taskInfo, w));
542
545
  const taskVis = Math.min(taskInfo.length, w);
543
546
 
@@ -627,22 +630,35 @@ export function createOrchWidget(
627
630
 
628
631
  // ── Phase-specific rendering ──────────────────
629
632
  const phaseIcon =
630
- vm.phase === "launching" ? "◌"
631
- : vm.phase === "planning" ? "◌"
632
- : vm.phase === "executing" ? "●"
633
- : vm.phase === "merging" ? "🔀"
634
- : vm.phase === "paused" ? "⏸"
635
- : vm.phase === "stopped" ? ""
636
- : vm.phase === "completed" ? "✓"
637
- : vm.phase === "failed" ? ""
638
- : "";
633
+ vm.phase === "launching"
634
+ ? "◌"
635
+ : vm.phase === "planning"
636
+ ? ""
637
+ : vm.phase === "executing"
638
+ ? ""
639
+ : vm.phase === "merging"
640
+ ? "🔀"
641
+ : vm.phase === "paused"
642
+ ? "⏸"
643
+ : vm.phase === "stopped"
644
+ ? "⛔"
645
+ : vm.phase === "completed"
646
+ ? "✓"
647
+ : vm.phase === "failed"
648
+ ? "✗"
649
+ : "○";
639
650
  const phaseColor =
640
- vm.phase === "executing" ? "accent"
641
- : vm.phase === "merging" ? "accent"
642
- : vm.phase === "completed" ? "success"
643
- : vm.phase === "failed" || vm.phase === "stopped" ? "error"
644
- : vm.phase === "paused" ? "warning"
645
- : "dim";
651
+ vm.phase === "executing"
652
+ ? "accent"
653
+ : vm.phase === "merging"
654
+ ? "accent"
655
+ : vm.phase === "completed"
656
+ ? "success"
657
+ : vm.phase === "failed" || vm.phase === "stopped"
658
+ ? "error"
659
+ : vm.phase === "paused"
660
+ ? "warning"
661
+ : "dim";
646
662
 
647
663
  // Header: phase icon + batch ID + wave + elapsed
648
664
  const header =
@@ -656,10 +672,7 @@ export function createOrchWidget(
656
672
 
657
673
  // ── Planning state ────────────────────────────
658
674
  if (vm.phase === "planning") {
659
- lines.push(truncateToWidth(
660
- theme.fg("dim", " ◌ Planning batch..."),
661
- width,
662
- ));
675
+ lines.push(truncateToWidth(theme.fg("dim", " ◌ Planning batch..."), width));
663
676
  return lines;
664
677
  }
665
678
 
@@ -683,18 +696,24 @@ export function createOrchWidget(
683
696
  // ── Summary counts line ───────────────────────
684
697
  const countParts: string[] = [];
685
698
  if (vm.summary.completed > 0) countParts.push(theme.fg("success", `${vm.summary.completed} ✓`));
686
- if (vm.summary.running > 0) countParts.push(theme.fg("accent", `${vm.summary.running} running`));
699
+ if (vm.summary.running > 0)
700
+ countParts.push(theme.fg("accent", `${vm.summary.running} running`));
687
701
  if (vm.summary.queued > 0) countParts.push(theme.fg("dim", `${vm.summary.queued} queued`));
688
702
  if (vm.summary.failed > 0) countParts.push(theme.fg("error", `${vm.summary.failed} ✗`));
689
- if (vm.summary.blocked > 0) countParts.push(theme.fg("warning", `${vm.summary.blocked} blocked`));
690
- if (vm.summary.stalled > 0) countParts.push(theme.fg("warning", `${vm.summary.stalled} stalled`));
703
+ if (vm.summary.blocked > 0)
704
+ countParts.push(theme.fg("warning", `${vm.summary.blocked} blocked`));
705
+ if (vm.summary.stalled > 0)
706
+ countParts.push(theme.fg("warning", `${vm.summary.stalled} stalled`));
691
707
  if (countParts.length > 0) {
692
708
  lines.push(truncateToWidth(" " + countParts.join(theme.fg("dim", " · ")), width));
693
709
  }
694
710
  lines.push("");
695
711
 
696
712
  // ── Lane cards ─────────────────────────────────
697
- if (vm.laneCards.length > 0 && (vm.phase === "executing" || vm.phase === "merging" || vm.phase === "paused")) {
713
+ if (
714
+ vm.laneCards.length > 0 &&
715
+ (vm.phase === "executing" || vm.phase === "merging" || vm.phase === "paused")
716
+ ) {
698
717
  const arrowWidth = 3;
699
718
  const minCardWidth = 18;
700
719
  const maxCols = Math.max(1, Math.floor((width + arrowWidth) / (minCardWidth + arrowWidth)));
@@ -703,7 +722,7 @@ export function createOrchWidget(
703
722
 
704
723
  for (let rowStart = 0; rowStart < vm.laneCards.length; rowStart += cols) {
705
724
  const rowCards = vm.laneCards.slice(rowStart, rowStart + cols);
706
- const rendered = rowCards.map(c => renderLaneCard(c, colWidth, theme));
725
+ const rendered = rowCards.map((c) => renderLaneCard(c, colWidth, theme));
707
726
 
708
727
  if (rendered.length > 0) {
709
728
  const cardHeight = rendered[0].length;
@@ -721,47 +740,41 @@ export function createOrchWidget(
721
740
 
722
741
  // ── Terminal states (completed/failed/stopped) ──
723
742
  if (vm.phase === "completed") {
724
- lines.push(truncateToWidth(
725
- theme.fg("success", " ✅ Batch complete"),
726
- width,
727
- ));
743
+ lines.push(truncateToWidth(theme.fg("success", " ✅ Batch complete"), width));
728
744
  } else if (vm.phase === "failed") {
729
- lines.push(truncateToWidth(
730
- theme.fg("error", " ❌ Batch failed"),
731
- width,
732
- ));
745
+ lines.push(truncateToWidth(theme.fg("error", " ❌ Batch failed"), width));
733
746
  for (const err of vm.errors.slice(0, 3)) {
734
- lines.push(truncateToWidth(
735
- theme.fg("error", ` ${err.slice(0, 80)}`),
736
- width,
737
- ));
747
+ lines.push(truncateToWidth(theme.fg("error", ` ${err.slice(0, 80)}`), width));
738
748
  }
739
749
  } else if (vm.phase === "stopped") {
740
- lines.push(truncateToWidth(
741
- theme.fg("error", ` ⛔ Stopped by ${vm.failurePolicy || "policy"}`),
742
- width,
743
- ));
750
+ lines.push(
751
+ truncateToWidth(theme.fg("error", ` ⛔ Stopped by ${vm.failurePolicy || "policy"}`), width),
752
+ );
744
753
  } else if (vm.phase === "merging") {
745
754
  lines.push("");
746
- lines.push(truncateToWidth(
747
- theme.fg("accent", ` 🔀 Merging lane branches into ${vm.orchBranch || "orch branch"}...`),
748
- width,
749
- ));
755
+ lines.push(
756
+ truncateToWidth(
757
+ theme.fg("accent", ` 🔀 Merging lane branches into ${vm.orchBranch || "orch branch"}...`),
758
+ width,
759
+ ),
760
+ );
750
761
  } else if (vm.phase === "paused") {
751
762
  lines.push("");
752
- lines.push(truncateToWidth(
753
- theme.fg("warning", " ⏸ Batch paused — lanes will stop after current tasks"),
754
- width,
755
- ));
763
+ lines.push(
764
+ truncateToWidth(
765
+ theme.fg("warning", " ⏸ Batch paused — lanes will stop after current tasks"),
766
+ width,
767
+ ),
768
+ );
756
769
  }
757
770
 
758
771
  // ── Footer: attach hint ───────────────────────
759
- if (vm.attachHint && (vm.phase === "executing" || vm.phase === "merging" || vm.phase === "paused")) {
772
+ if (
773
+ vm.attachHint &&
774
+ (vm.phase === "executing" || vm.phase === "merging" || vm.phase === "paused")
775
+ ) {
760
776
  lines.push("");
761
- lines.push(truncateToWidth(
762
- theme.fg("dim", ` 💡 ${vm.attachHint}`),
763
- width,
764
- ));
777
+ lines.push(truncateToWidth(theme.fg("dim", ` 💡 ${vm.attachHint}`), width));
765
778
  }
766
779
 
767
780
  return lines;
@@ -770,4 +783,3 @@ export function createOrchWidget(
770
783
  };
771
784
  };
772
785
  }
773
-
@@ -4,7 +4,6 @@
4
4
  */
5
5
  import { execFileSync } from "child_process";
6
6
 
7
-
8
7
  // ── Branch Helpers ───────────────────────────────────────────────────
9
8
 
10
9
  /**
@@ -87,4 +86,3 @@ export function runGitWithEnv(
87
86
  };
88
87
  }
89
88
  }
90
-