@pushpalsdev/cli 1.1.7 → 1.1.9
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/dist/pushpals-cli.js
CHANGED
|
@@ -1644,6 +1644,7 @@ var WINDOWS_TASKKILL_TIMEOUT_MS2 = 5000;
|
|
|
1644
1644
|
var RUNTIME_BINARY_DOWNLOAD_ATTEMPTS = 3;
|
|
1645
1645
|
var DEFAULT_STARTUP_GIT_PROBE_TIMEOUT_MS = 5000;
|
|
1646
1646
|
var DEFAULT_STARTUP_GIT_REMOTE_TIMEOUT_MS = 1e4;
|
|
1647
|
+
var DEFAULT_EMBEDDED_SERVICE_LAUNCH_WARN_MS = 5000;
|
|
1647
1648
|
var EMBEDDED_SERVICE_RESTART_MAX_ATTEMPTS = 4;
|
|
1648
1649
|
var WORKERPAL_STARTUP_READINESS_PROBE_MAX_MS = 15000;
|
|
1649
1650
|
var EMBEDDED_RUNTIME_SAFETY_CAP_DISABLE_ENV = "PUSHPALS_DISABLE_EMBEDDED_SAFETY_CAPS";
|
|
@@ -1677,6 +1678,13 @@ function formatRuntimeStartupTimingSummary(input) {
|
|
|
1677
1678
|
const detail = typeof input.detail === "string" && input.detail.trim() ? ` detail=${input.detail.trim()}` : "";
|
|
1678
1679
|
return `[pushpals] startup timing summary: outcome=${input.outcome} ` + `total=${Math.max(0, Math.floor(input.totalDurationMs))}ms${detail}` + (phaseSummary ? ` ${phaseSummary}` : "");
|
|
1679
1680
|
}
|
|
1681
|
+
function formatEmbeddedServiceLaunchDelayWarning(input) {
|
|
1682
|
+
const durationMs = Math.max(0, Math.floor(Number(input.durationMs) || 0));
|
|
1683
|
+
const serviceName = String(input.serviceName || "service");
|
|
1684
|
+
const platform = String(input.platform ?? process.platform);
|
|
1685
|
+
const windowsHint = platform === "win32" ? " On Windows, first-run standalone binaries can be delayed while security software scans them." : "";
|
|
1686
|
+
return `[pushpals] Embedded ${serviceName} process launch took ${durationMs}ms; startup is continuing.` + windowsHint;
|
|
1687
|
+
}
|
|
1680
1688
|
function describeWorkerExecutionReadiness(opts) {
|
|
1681
1689
|
const onlineWorkers = Math.max(0, Math.floor(opts.onlineWorkers));
|
|
1682
1690
|
const idleWorkers = Math.max(0, Math.floor(opts.idleWorkers));
|
|
@@ -4481,10 +4489,34 @@ async function autoStartRuntimeServices(opts) {
|
|
|
4481
4489
|
};
|
|
4482
4490
|
};
|
|
4483
4491
|
const launchService = (name, command, launchOpts) => {
|
|
4484
|
-
|
|
4492
|
+
const launchStartedAt = Date.now();
|
|
4493
|
+
const service = serviceManager.startService(buildManagedServiceSpec(name, command, launchOpts));
|
|
4494
|
+
const launchDurationMs = Date.now() - launchStartedAt;
|
|
4495
|
+
if (launchDurationMs >= DEFAULT_EMBEDDED_SERVICE_LAUNCH_WARN_MS) {
|
|
4496
|
+
const warning = formatEmbeddedServiceLaunchDelayWarning({
|
|
4497
|
+
serviceName: name,
|
|
4498
|
+
durationMs: launchDurationMs,
|
|
4499
|
+
platform: process.platform
|
|
4500
|
+
});
|
|
4501
|
+
console.warn(warning);
|
|
4502
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, warning);
|
|
4503
|
+
}
|
|
4504
|
+
return service;
|
|
4485
4505
|
};
|
|
4486
4506
|
const replaceService = (name, command, launchOpts) => {
|
|
4487
|
-
|
|
4507
|
+
const launchStartedAt = Date.now();
|
|
4508
|
+
const service = serviceManager.replaceService(buildManagedServiceSpec(name, command, launchOpts));
|
|
4509
|
+
const launchDurationMs = Date.now() - launchStartedAt;
|
|
4510
|
+
if (launchDurationMs >= DEFAULT_EMBEDDED_SERVICE_LAUNCH_WARN_MS) {
|
|
4511
|
+
const warning = formatEmbeddedServiceLaunchDelayWarning({
|
|
4512
|
+
serviceName: name,
|
|
4513
|
+
durationMs: launchDurationMs,
|
|
4514
|
+
platform: process.platform
|
|
4515
|
+
});
|
|
4516
|
+
console.warn(warning);
|
|
4517
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, warning);
|
|
4518
|
+
}
|
|
4519
|
+
return service;
|
|
4488
4520
|
};
|
|
4489
4521
|
const sandboxPaths = buildWorkerpalSandboxPaths(runtimeRoot);
|
|
4490
4522
|
const remoteBuddyFallbackBun = process.platform === "win32" ? resolveEmbeddedBunExecutableFromEnv(runtimeEnv, process.platform, process.execPath) : "";
|
|
@@ -5905,6 +5937,7 @@ export {
|
|
|
5905
5937
|
formatTimestampedCliLine,
|
|
5906
5938
|
formatSessionEventLine,
|
|
5907
5939
|
formatRuntimeStartupTimingSummary,
|
|
5940
|
+
formatEmbeddedServiceLaunchDelayWarning,
|
|
5908
5941
|
formatEmbeddedRuntimeHealthLines2 as formatEmbeddedRuntimeHealthLines,
|
|
5909
5942
|
extractRemoteBuddySessionConsumerHealth,
|
|
5910
5943
|
extractRemoteBuddyAutonomousEngineState,
|
package/package.json
CHANGED
|
@@ -2245,11 +2245,14 @@ export function collectQualityGateValidationCommands(params: {
|
|
|
2245
2245
|
planning: TaskExecutePlanning;
|
|
2246
2246
|
changedTestPaths: string[];
|
|
2247
2247
|
isTestTask: boolean;
|
|
2248
|
+
repo?: string;
|
|
2249
|
+
changedPaths?: string[];
|
|
2248
2250
|
}): {
|
|
2249
2251
|
commandsToRun: string[];
|
|
2250
2252
|
requiredRunnableSteps: string[];
|
|
2251
2253
|
plannerRunnableSteps: string[];
|
|
2252
2254
|
fallbackValidationSteps: string[];
|
|
2255
|
+
inferredRepoNativeValidationSteps: string[];
|
|
2253
2256
|
} {
|
|
2254
2257
|
const requiredRunnableSteps = runnableValidationCommandsFromSteps(
|
|
2255
2258
|
params.planning.requiredValidationSteps,
|
|
@@ -2266,15 +2269,20 @@ export function collectQualityGateValidationCommands(params: {
|
|
|
2266
2269
|
params.changedTestPaths,
|
|
2267
2270
|
)
|
|
2268
2271
|
: [];
|
|
2272
|
+
const inferredRepoNativeValidationSteps = params.repo
|
|
2273
|
+
? inferRepoNativeValidationCommands(params.repo, params.changedPaths ?? [])
|
|
2274
|
+
: [];
|
|
2269
2275
|
const commandsToRun = dedupeValidationCommands(
|
|
2270
2276
|
requiredRunnableSteps,
|
|
2271
2277
|
plannerRunnableSteps.length > 0 ? plannerRunnableSteps : fallbackValidationSteps,
|
|
2278
|
+
inferredRepoNativeValidationSteps,
|
|
2272
2279
|
).slice(0, 16);
|
|
2273
2280
|
return {
|
|
2274
2281
|
commandsToRun,
|
|
2275
2282
|
requiredRunnableSteps,
|
|
2276
2283
|
plannerRunnableSteps,
|
|
2277
2284
|
fallbackValidationSteps,
|
|
2285
|
+
inferredRepoNativeValidationSteps,
|
|
2278
2286
|
};
|
|
2279
2287
|
}
|
|
2280
2288
|
|
|
@@ -2416,6 +2424,114 @@ function hasBalancedPositiveNegativeAssertions(paths: string[], repo: string): b
|
|
|
2416
2424
|
return positiveAssertions > 0 && negativeAssertions > 0;
|
|
2417
2425
|
}
|
|
2418
2426
|
|
|
2427
|
+
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
2428
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
2429
|
+
return value as Record<string, unknown>;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
function changedPathMentionsGuidance(pathPattern: RegExp, guidance: string): boolean {
|
|
2433
|
+
return pathPattern.test(guidance);
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
export function collectPrePublishHygieneIssues(params: {
|
|
2437
|
+
repo: string;
|
|
2438
|
+
changedPaths: string[];
|
|
2439
|
+
instruction: string;
|
|
2440
|
+
targetPath?: string;
|
|
2441
|
+
planning: TaskExecutePlanning;
|
|
2442
|
+
reviewAgent?: Record<string, unknown> | null;
|
|
2443
|
+
}): string[] {
|
|
2444
|
+
const changedPaths = params.changedPaths.map((path) => path.replace(/\\/g, "/"));
|
|
2445
|
+
const changedPathSet = new Set(changedPaths);
|
|
2446
|
+
const guidance = [
|
|
2447
|
+
params.instruction,
|
|
2448
|
+
params.targetPath ?? "",
|
|
2449
|
+
...(params.planning.targetPaths ?? []),
|
|
2450
|
+
...(params.planning.scope.writeGlobs ?? []),
|
|
2451
|
+
...(params.planning.acceptanceCriteria ?? []),
|
|
2452
|
+
...(params.planning.validationSteps ?? []),
|
|
2453
|
+
...((params.reviewAgent?.reviewerFindings as string[] | undefined) ?? []),
|
|
2454
|
+
]
|
|
2455
|
+
.join("\n")
|
|
2456
|
+
.toLowerCase();
|
|
2457
|
+
const issues: string[] = [];
|
|
2458
|
+
|
|
2459
|
+
if (
|
|
2460
|
+
changedPathSet.has(".gitignore") &&
|
|
2461
|
+
!changedPathMentionsGuidance(/\b(gitignore|ignore file|node_modules|dependency cache)\b/i, guidance)
|
|
2462
|
+
) {
|
|
2463
|
+
issues.push(
|
|
2464
|
+
"modified .gitignore without task or reviewer guidance requesting ignore-policy changes.",
|
|
2465
|
+
);
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
if (changedPathSet.has("tests/reactNativeMock.ts")) {
|
|
2469
|
+
const changedTestPaths = changedPaths.filter((path) => isAssertionCoverageTestPath(path));
|
|
2470
|
+
const hasConsumerInChangedTests = changedTestPaths.some((rel) => {
|
|
2471
|
+
try {
|
|
2472
|
+
return /reactNativeMock/i.test(readFileSync(resolve(params.repo, rel), "utf8"));
|
|
2473
|
+
} catch {
|
|
2474
|
+
return false;
|
|
2475
|
+
}
|
|
2476
|
+
});
|
|
2477
|
+
const explicitlyRequested = changedPathMentionsGuidance(/reactnativemock|react native mock/i, guidance);
|
|
2478
|
+
if (!hasConsumerInChangedTests && !explicitlyRequested) {
|
|
2479
|
+
issues.push(
|
|
2480
|
+
"changed tests/reactNativeMock.ts without a changed test importing it or explicit reviewer guidance.",
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
if (changedPaths.some((path) => /(^|\/)node_modules(\/|$)/i.test(path))) {
|
|
2486
|
+
issues.push("attempted to publish node_modules changes; dependency installs must not become PR content.");
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
return Array.from(new Set(issues));
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
export function inferRepoNativeValidationCommands(repo: string, changedPaths: string[]): string[] {
|
|
2493
|
+
const packageJsonPath = resolve(repo, "package.json");
|
|
2494
|
+
if (!existsSync(packageJsonPath)) return [];
|
|
2495
|
+
|
|
2496
|
+
let packageJson: {
|
|
2497
|
+
scripts?: Record<string, unknown>;
|
|
2498
|
+
dependencies?: Record<string, unknown>;
|
|
2499
|
+
devDependencies?: Record<string, unknown>;
|
|
2500
|
+
} = {};
|
|
2501
|
+
try {
|
|
2502
|
+
packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
2503
|
+
} catch {
|
|
2504
|
+
return [];
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
const scripts = packageJson.scripts ?? {};
|
|
2508
|
+
const dependencies = {
|
|
2509
|
+
...(packageJson.dependencies ?? {}),
|
|
2510
|
+
...(packageJson.devDependencies ?? {}),
|
|
2511
|
+
};
|
|
2512
|
+
const normalizedPaths = changedPaths.map((path) => path.replace(/\\/g, "/"));
|
|
2513
|
+
const hasNonDocChange = normalizedPaths.some((path) => !/\.(?:md|mdx|txt)$/i.test(path));
|
|
2514
|
+
const hasTsChange = normalizedPaths.some((path) => /\.[cm]?tsx?$/i.test(path));
|
|
2515
|
+
const commands: string[] = [];
|
|
2516
|
+
|
|
2517
|
+
if (hasTsChange) {
|
|
2518
|
+
if (typeof scripts.typecheck === "string" && scripts.typecheck.trim()) {
|
|
2519
|
+
commands.push("bun run typecheck");
|
|
2520
|
+
} else if (
|
|
2521
|
+
existsSync(resolve(repo, "tsconfig.json")) ||
|
|
2522
|
+
Object.prototype.hasOwnProperty.call(dependencies, "typescript")
|
|
2523
|
+
) {
|
|
2524
|
+
commands.push("bun x tsc --noEmit");
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
if (hasNonDocChange && typeof scripts.lint === "string" && scripts.lint.trim()) {
|
|
2529
|
+
commands.push("bun run lint");
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
return dedupeValidationCommands(commands).slice(0, 4);
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2419
2535
|
async function runDeterministicQualityGate(
|
|
2420
2536
|
repo: string,
|
|
2421
2537
|
params: Record<string, unknown>,
|
|
@@ -2485,6 +2601,16 @@ async function runDeterministicQualityGate(
|
|
|
2485
2601
|
if (!statusResult.ok) {
|
|
2486
2602
|
addScopeIssue("could not evaluate changed paths from git status.");
|
|
2487
2603
|
}
|
|
2604
|
+
for (const issue of collectPrePublishHygieneIssues({
|
|
2605
|
+
repo,
|
|
2606
|
+
changedPaths,
|
|
2607
|
+
instruction,
|
|
2608
|
+
targetPath,
|
|
2609
|
+
planning,
|
|
2610
|
+
reviewAgent: asRecord(params.reviewAgent ?? params.review_agent),
|
|
2611
|
+
})) {
|
|
2612
|
+
addScopeIssue(issue);
|
|
2613
|
+
}
|
|
2488
2614
|
for (const issue of collectWriteScopeIssuesFromChangedPaths(changedPaths, planning)) {
|
|
2489
2615
|
addScopeIssue(issue);
|
|
2490
2616
|
}
|
|
@@ -2525,6 +2651,8 @@ async function runDeterministicQualityGate(
|
|
|
2525
2651
|
planning,
|
|
2526
2652
|
changedTestPaths,
|
|
2527
2653
|
isTestTask,
|
|
2654
|
+
repo,
|
|
2655
|
+
changedPaths,
|
|
2528
2656
|
});
|
|
2529
2657
|
const validationRuns: ValidationExecutionResult[] = [];
|
|
2530
2658
|
const outputPolicy = outputPolicyForRuntime(runtimeConfig);
|
|
@@ -4478,54 +4606,104 @@ export async function resumePreparedMergeConflictRebase(
|
|
|
4478
4606
|
};
|
|
4479
4607
|
}
|
|
4480
4608
|
|
|
4481
|
-
|
|
4482
|
-
let
|
|
4483
|
-
|
|
4484
|
-
rebaseContinue = await git(repo, ["-c", "core.editor=true", "rebase", "--continue"]);
|
|
4485
|
-
continueOutput = combinedGitOutput(rebaseContinue);
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4609
|
+
const maxContinuationPasses = Math.max(1, MAX_MERGE_CONFLICT_RESOLUTION_PASSES);
|
|
4610
|
+
let lastContinueOutput = "";
|
|
4611
|
+
for (let pass = 1; pass <= maxContinuationPasses; pass += 1) {
|
|
4612
|
+
let rebaseContinue = await git(repo, ["-c", "core.editor=true", "rebase", "--continue"]);
|
|
4613
|
+
let continueOutput = combinedGitOutput(rebaseContinue);
|
|
4614
|
+
if (!rebaseContinue.ok && isRebaseEditorPromptOutput(continueOutput)) {
|
|
4615
|
+
rebaseContinue = await git(repo, ["-c", "core.editor=true", "rebase", "--continue"]);
|
|
4616
|
+
continueOutput = combinedGitOutput(rebaseContinue);
|
|
4617
|
+
}
|
|
4618
|
+
lastContinueOutput = continueOutput;
|
|
4619
|
+
|
|
4620
|
+
if (!rebaseContinue.ok) {
|
|
4621
|
+
if (/no rebase in progress/i.test(continueOutput)) {
|
|
4622
|
+
onLog?.(
|
|
4623
|
+
"stdout",
|
|
4624
|
+
"[MergeConflict] Prepared rebase was already complete after continuation.",
|
|
4625
|
+
);
|
|
4626
|
+
return { ok: true, resumed: true, sequencer: null };
|
|
4627
|
+
}
|
|
4628
|
+
if (/no changes - did you forget to use 'git add'|nothing to commit/i.test(continueOutput)) {
|
|
4629
|
+
const rebaseSkip = await git(repo, ["rebase", "--skip"]);
|
|
4630
|
+
const skipOutput = combinedGitOutput(rebaseSkip);
|
|
4631
|
+
lastContinueOutput = skipOutput || continueOutput;
|
|
4632
|
+
if (!rebaseSkip.ok && !isRebaseConflictOutput(skipOutput)) {
|
|
4498
4633
|
return {
|
|
4499
|
-
ok:
|
|
4500
|
-
|
|
4501
|
-
sequencer: "rebase",
|
|
4502
|
-
detail: `rebase advanced into another conflicted commit with ${nextPaths.length} unresolved file(s)`,
|
|
4503
|
-
advancedToNextConflict: true,
|
|
4634
|
+
ok: false,
|
|
4635
|
+
error: `Failed to skip empty prepared merge-conflict rebase commit: ${skipOutput}`,
|
|
4504
4636
|
};
|
|
4505
4637
|
}
|
|
4638
|
+
} else {
|
|
4639
|
+
const continuingSequencer = await activeGitOperation(repo);
|
|
4640
|
+
if (continuingSequencer === "rebase") {
|
|
4641
|
+
const nextUnresolved = await git(repo, ["diff", "--name-only", "--diff-filter=U"]);
|
|
4642
|
+
if (nextUnresolved.ok) {
|
|
4643
|
+
const nextPaths = parseChangedPathsFromNameOnlyOutput(nextUnresolved.stdout);
|
|
4644
|
+
if (nextPaths.length > 0) {
|
|
4645
|
+
onLog?.(
|
|
4646
|
+
"stdout",
|
|
4647
|
+
`[MergeConflict] Rebase advanced into another conflicted commit with ${nextPaths.length} unresolved file(s); rerunning the resolver on updated sandbox state.`,
|
|
4648
|
+
);
|
|
4649
|
+
return {
|
|
4650
|
+
ok: true,
|
|
4651
|
+
resumed: true,
|
|
4652
|
+
sequencer: "rebase",
|
|
4653
|
+
detail: `rebase advanced into another conflicted commit with ${nextPaths.length} unresolved file(s)`,
|
|
4654
|
+
advancedToNextConflict: true,
|
|
4655
|
+
};
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
return {
|
|
4660
|
+
ok: false,
|
|
4661
|
+
error: `Failed to continue prepared merge-conflict rebase: ${continueOutput}`,
|
|
4662
|
+
};
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
|
|
4666
|
+
const remainingSequencer = await activeGitOperation(repo);
|
|
4667
|
+
if (!remainingSequencer) {
|
|
4668
|
+
onLog?.(
|
|
4669
|
+
"stdout",
|
|
4670
|
+
"[MergeConflict] Auto-continued the prepared rebase after the executor returned with no unresolved conflicts.",
|
|
4671
|
+
);
|
|
4672
|
+
return { ok: true, resumed: true, sequencer: null };
|
|
4673
|
+
}
|
|
4674
|
+
if (remainingSequencer !== "rebase") {
|
|
4675
|
+
return { ok: true, resumed: true, sequencer: remainingSequencer };
|
|
4676
|
+
}
|
|
4677
|
+
|
|
4678
|
+
const nextUnresolved = await git(repo, ["diff", "--name-only", "--diff-filter=U"]);
|
|
4679
|
+
if (nextUnresolved.ok) {
|
|
4680
|
+
const nextPaths = parseChangedPathsFromNameOnlyOutput(nextUnresolved.stdout);
|
|
4681
|
+
if (nextPaths.length > 0) {
|
|
4682
|
+
onLog?.(
|
|
4683
|
+
"stdout",
|
|
4684
|
+
`[MergeConflict] Rebase advanced into another conflicted commit with ${nextPaths.length} unresolved file(s); rerunning the resolver on updated sandbox state.`,
|
|
4685
|
+
);
|
|
4686
|
+
return {
|
|
4687
|
+
ok: true,
|
|
4688
|
+
resumed: true,
|
|
4689
|
+
sequencer: "rebase",
|
|
4690
|
+
detail: `rebase advanced into another conflicted commit with ${nextPaths.length} unresolved file(s)`,
|
|
4691
|
+
advancedToNextConflict: true,
|
|
4692
|
+
};
|
|
4506
4693
|
}
|
|
4507
4694
|
}
|
|
4508
|
-
return {
|
|
4509
|
-
ok: false,
|
|
4510
|
-
error: `Failed to continue prepared merge-conflict rebase: ${continueOutput}`,
|
|
4511
|
-
};
|
|
4512
|
-
}
|
|
4513
4695
|
|
|
4514
|
-
const remainingSequencer = await activeGitOperation(repo);
|
|
4515
|
-
if (!remainingSequencer) {
|
|
4516
4696
|
onLog?.(
|
|
4517
4697
|
"stdout",
|
|
4518
|
-
|
|
4698
|
+
`[MergeConflict] Rebase still active after continuation pass ${pass}/${maxContinuationPasses}; trying another non-interactive continue.`,
|
|
4519
4699
|
);
|
|
4520
4700
|
}
|
|
4701
|
+
|
|
4521
4702
|
return {
|
|
4522
|
-
ok:
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
remainingSequencer === "rebase"
|
|
4527
|
-
? "rebase advanced but another continuation step is still required"
|
|
4528
|
-
: undefined,
|
|
4703
|
+
ok: false,
|
|
4704
|
+
error:
|
|
4705
|
+
`Prepared merge-conflict rebase remained active after ${maxContinuationPasses} continuation pass(es).` +
|
|
4706
|
+
(lastContinueOutput ? ` Last output: ${lastContinueOutput}` : ""),
|
|
4529
4707
|
};
|
|
4530
4708
|
}
|
|
4531
4709
|
|