@lumpcode/core 0.0.15 → 0.1.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.
- package/README.md +7 -1
- package/dist/helpers/execBinary/main.d.ts +19 -5
- package/dist/helpers/execBinary/main.d.ts.map +1 -1
- package/dist/helpers/executeStepsForContextList/main.d.ts +10 -3
- package/dist/helpers/executeStepsForContextList/main.d.ts.map +1 -1
- package/dist/helpers/getContextStatus/main.d.ts +5 -0
- package/dist/helpers/getContextStatus/main.d.ts.map +1 -1
- package/dist/helpers/getToDoContextList/main.d.ts +6 -0
- package/dist/helpers/getToDoContextList/main.d.ts.map +1 -1
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/refreshRemoteTrackingRefs/index.d.ts +2 -0
- package/dist/helpers/refreshRemoteTrackingRefs/index.d.ts.map +1 -0
- package/dist/helpers/refreshRemoteTrackingRefs/main.d.ts +11 -0
- package/dist/helpers/refreshRemoteTrackingRefs/main.d.ts.map +1 -0
- package/dist/index.cjs +469 -172
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +467 -174
- package/dist/index.js.map +1 -1
- package/dist/testing/processTreeChild.d.cts +2 -0
- package/dist/testing/processTreeChild.d.cts.map +1 -0
- package/dist/testing/processTreeTestHelpers.d.ts +12 -0
- package/dist/testing/processTreeTestHelpers.d.ts.map +1 -0
- package/dist/testing/sigtermIgnorantTreeChild.d.cts +2 -0
- package/dist/testing/sigtermIgnorantTreeChild.d.cts.map +1 -0
- package/dist/types/ExecShellFn.d.ts +16 -0
- package/dist/types/ExecShellFn.d.ts.map +1 -0
- package/dist/types/ExecuteStepsFailureData.d.ts +6 -0
- package/dist/types/ExecuteStepsFailureData.d.ts.map +1 -0
- package/dist/types/ExecuteStepsFailureReason.d.ts +2 -0
- package/dist/types/ExecuteStepsFailureReason.d.ts.map +1 -0
- package/dist/types/GitAddCommandFn.d.ts +7 -1
- package/dist/types/GitAddCommandFn.d.ts.map +1 -1
- package/dist/types/GitCommitCommandFn.d.ts +7 -1
- package/dist/types/GitCommitCommandFn.d.ts.map +1 -1
- package/dist/types/GitPushCommandFn.d.ts +7 -1
- package/dist/types/GitPushCommandFn.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/usages/runLump/main.d.ts +10 -5
- package/dist/usages/runLump/main.d.ts.map +1 -1
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/isProcessAlive/index.d.ts +2 -1
- package/dist/utils/isProcessAlive/index.d.ts.map +1 -1
- package/dist/utils/killProcessTree/index.d.ts +2 -0
- package/dist/utils/killProcessTree/index.d.ts.map +1 -0
- package/dist/utils/killProcessTree/main.d.ts +11 -0
- package/dist/utils/killProcessTree/main.d.ts.map +1 -0
- package/dist/utils/nodeErrnoCode/index.d.ts +1 -1
- package/dist/utils/nodeErrnoCode/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { exec, spawn } from 'node:child_process';
|
|
1
|
+
import { execFile, exec, spawn } from 'node:child_process';
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
4
|
import * as path from 'node:path';
|
|
@@ -3403,6 +3403,152 @@ function parseGitLogHashSubjectLines(stdout) {
|
|
|
3403
3403
|
});
|
|
3404
3404
|
}
|
|
3405
3405
|
|
|
3406
|
+
/** Node-style `code` from a caught unknown error (e.g. `ENOENT`), or undefined. */
|
|
3407
|
+
function nodeErrnoCode(error) {
|
|
3408
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
|
3409
|
+
const code = error.code;
|
|
3410
|
+
return typeof code === 'string' ? code : undefined;
|
|
3411
|
+
}
|
|
3412
|
+
return undefined;
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
/** Returns whether a process id is still running (signal 0 probe). */
|
|
3416
|
+
function isProcessAlive(pid, options) {
|
|
3417
|
+
const onProbeError = options?.onProbeError ?? 'throw';
|
|
3418
|
+
try {
|
|
3419
|
+
process.kill(pid, 0);
|
|
3420
|
+
return true;
|
|
3421
|
+
}
|
|
3422
|
+
catch (error) {
|
|
3423
|
+
if (nodeErrnoCode(error) === 'ESRCH')
|
|
3424
|
+
return false;
|
|
3425
|
+
if (onProbeError === 'throw')
|
|
3426
|
+
throw error;
|
|
3427
|
+
return onProbeError === 'alive';
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
const execFileAsync = promisify(execFile);
|
|
3432
|
+
function sleep(ms) {
|
|
3433
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3434
|
+
}
|
|
3435
|
+
async function listUnixProcessTreePids(rootPid) {
|
|
3436
|
+
const { stdout } = await execFileAsync('ps', ['-eo', 'pid=,ppid=']);
|
|
3437
|
+
const childrenByParent = new Map();
|
|
3438
|
+
for (const line of stdout.split('\n')) {
|
|
3439
|
+
const trimmed = line.trim();
|
|
3440
|
+
if (!trimmed)
|
|
3441
|
+
continue;
|
|
3442
|
+
const parts = trimmed.split(/\s+/);
|
|
3443
|
+
if (parts.length < 2)
|
|
3444
|
+
continue;
|
|
3445
|
+
const pid = Number.parseInt(parts[0] ?? '', 10);
|
|
3446
|
+
const ppid = Number.parseInt(parts[1] ?? '', 10);
|
|
3447
|
+
if (Number.isNaN(pid) || Number.isNaN(ppid))
|
|
3448
|
+
continue;
|
|
3449
|
+
const siblings = childrenByParent.get(ppid) ?? [];
|
|
3450
|
+
siblings.push(pid);
|
|
3451
|
+
childrenByParent.set(ppid, siblings);
|
|
3452
|
+
}
|
|
3453
|
+
const ordered = [];
|
|
3454
|
+
const queue = [rootPid];
|
|
3455
|
+
while (queue.length > 0) {
|
|
3456
|
+
const pid = queue.shift();
|
|
3457
|
+
if (pid === undefined)
|
|
3458
|
+
break;
|
|
3459
|
+
ordered.push(pid);
|
|
3460
|
+
const children = childrenByParent.get(pid) ?? [];
|
|
3461
|
+
for (const childPid of children) {
|
|
3462
|
+
queue.push(childPid);
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
return ordered;
|
|
3466
|
+
}
|
|
3467
|
+
function killPid(pid, signal) {
|
|
3468
|
+
try {
|
|
3469
|
+
process.kill(pid, signal);
|
|
3470
|
+
}
|
|
3471
|
+
catch (error) {
|
|
3472
|
+
const code = nodeErrnoCode(error);
|
|
3473
|
+
if (code !== 'ESRCH') {
|
|
3474
|
+
throw error;
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
async function killUnixProcessTreeImmediate(rootPid) {
|
|
3479
|
+
const pids = await listUnixProcessTreePids(rootPid);
|
|
3480
|
+
for (let index = pids.length - 1; index >= 0; index -= 1) {
|
|
3481
|
+
killPid(pids[index], 'SIGKILL');
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
async function killUnixProcessTreeWithGrace(rootPid, graceMs) {
|
|
3485
|
+
const pids = await listUnixProcessTreePids(rootPid);
|
|
3486
|
+
for (let index = pids.length - 1; index >= 0; index -= 1) {
|
|
3487
|
+
killPid(pids[index], 'SIGTERM');
|
|
3488
|
+
}
|
|
3489
|
+
const deadline = Date.now() + graceMs;
|
|
3490
|
+
while (Date.now() < deadline) {
|
|
3491
|
+
const anyAlive = pids.some((pid) => isProcessAlive(pid, { onProbeError: 'dead' }));
|
|
3492
|
+
if (!anyAlive) {
|
|
3493
|
+
return;
|
|
3494
|
+
}
|
|
3495
|
+
await sleep(25);
|
|
3496
|
+
}
|
|
3497
|
+
const remaining = await listUnixProcessTreePids(rootPid);
|
|
3498
|
+
for (let index = remaining.length - 1; index >= 0; index -= 1) {
|
|
3499
|
+
killPid(remaining[index], 'SIGKILL');
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
async function killWindowsProcessTree(rootPid) {
|
|
3503
|
+
try {
|
|
3504
|
+
await execFileAsync('taskkill', ['/PID', String(rootPid), '/T', '/F'], {
|
|
3505
|
+
windowsHide: true,
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
catch (error) {
|
|
3509
|
+
const stderr = error && typeof error === 'object' && 'stderr' in error
|
|
3510
|
+
? String(error.stderr ?? '')
|
|
3511
|
+
: '';
|
|
3512
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3513
|
+
const combined = `${message}\n${stderr}`;
|
|
3514
|
+
if (/not found|no running instance|ne existe pas|introuvable/i.test(combined)) {
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
|
+
// taskkill can fail to terminate some descendants while the root exits (SEA/agent trees).
|
|
3518
|
+
if (!isProcessAlive(rootPid)) {
|
|
3519
|
+
return;
|
|
3520
|
+
}
|
|
3521
|
+
throw error;
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
/**
|
|
3525
|
+
* Kill a process and its descendants.
|
|
3526
|
+
* `graceMs` default 0 → immediate SIGKILL / taskkill /T /F.
|
|
3527
|
+
* When `graceMs > 0` (Unix), SIGTERM first, then SIGKILL after the grace window.
|
|
3528
|
+
*/
|
|
3529
|
+
async function killProcessTree(input) {
|
|
3530
|
+
const { pid, graceMs = 0 } = input;
|
|
3531
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
3532
|
+
return failure(`Invalid pid: ${pid}`);
|
|
3533
|
+
}
|
|
3534
|
+
try {
|
|
3535
|
+
if (process.platform === 'win32') {
|
|
3536
|
+
await killWindowsProcessTree(pid);
|
|
3537
|
+
}
|
|
3538
|
+
else if (graceMs > 0) {
|
|
3539
|
+
await killUnixProcessTreeWithGrace(pid, graceMs);
|
|
3540
|
+
}
|
|
3541
|
+
else {
|
|
3542
|
+
await killUnixProcessTreeImmediate(pid);
|
|
3543
|
+
}
|
|
3544
|
+
return success(undefined);
|
|
3545
|
+
}
|
|
3546
|
+
catch (error) {
|
|
3547
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3548
|
+
return failure(`Could not kill process tree for pid ${pid}: ${message}`);
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
|
|
3406
3552
|
const execAsyncBase = promisify(exec);
|
|
3407
3553
|
async function execAsync(command, options) {
|
|
3408
3554
|
const { stdout, stderr, hasErrored } = await execAsyncBase(command, { cwd: options?.cwd })
|
|
@@ -3432,38 +3578,86 @@ async function execAsync(command, options) {
|
|
|
3432
3578
|
});
|
|
3433
3579
|
}
|
|
3434
3580
|
|
|
3435
|
-
function execBinary(
|
|
3581
|
+
function execBinary(input) {
|
|
3582
|
+
const { binaryPath, args, timeoutMillis = 1000 * 60 * 10, cwd, env, stdio, signal, killGraceMs = 5000, } = input;
|
|
3436
3583
|
return new Promise((resolve) => {
|
|
3437
3584
|
let settled = false;
|
|
3585
|
+
let canceling = false;
|
|
3586
|
+
let child;
|
|
3587
|
+
let stdout = '';
|
|
3588
|
+
let stderr = '';
|
|
3589
|
+
let timeout;
|
|
3438
3590
|
const finish = (result) => {
|
|
3439
3591
|
if (settled)
|
|
3440
3592
|
return;
|
|
3441
3593
|
settled = true;
|
|
3442
|
-
|
|
3594
|
+
if (timeout !== undefined)
|
|
3595
|
+
clearTimeout(timeout);
|
|
3596
|
+
if (signal) {
|
|
3597
|
+
signal.removeEventListener('abort', onAbort);
|
|
3598
|
+
}
|
|
3443
3599
|
resolve(result);
|
|
3444
3600
|
};
|
|
3445
|
-
const
|
|
3601
|
+
const cancelWith = async (reason, message) => {
|
|
3602
|
+
if (settled || canceling)
|
|
3603
|
+
return;
|
|
3604
|
+
canceling = true;
|
|
3605
|
+
const pid = child?.pid;
|
|
3606
|
+
if (pid != null) {
|
|
3607
|
+
await killProcessTree({ pid, graceMs: killGraceMs });
|
|
3608
|
+
}
|
|
3446
3609
|
finish(failure({
|
|
3447
|
-
message
|
|
3610
|
+
message,
|
|
3448
3611
|
binaryPath,
|
|
3449
3612
|
args,
|
|
3613
|
+
stdout,
|
|
3614
|
+
stderr,
|
|
3615
|
+
reason,
|
|
3450
3616
|
}));
|
|
3451
|
-
}
|
|
3617
|
+
};
|
|
3618
|
+
const onAbort = () => {
|
|
3619
|
+
void cancelWith('aborted', 'Process aborted');
|
|
3620
|
+
};
|
|
3621
|
+
if (signal?.aborted) {
|
|
3622
|
+
void cancelWith('aborted', 'Process aborted');
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3625
|
+
if (signal) {
|
|
3626
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
3627
|
+
}
|
|
3452
3628
|
const { executable: resolvedExecutable, args: resolvedArgs } = resolveSpawnExecutable(binaryPath, args);
|
|
3453
|
-
const
|
|
3454
|
-
|
|
3629
|
+
const spawnOptions = {
|
|
3630
|
+
...(cwd !== undefined ? { cwd } : {}),
|
|
3631
|
+
...(env !== undefined ? { env } : {}),
|
|
3632
|
+
...(stdio !== undefined ? { stdio } : {}),
|
|
3633
|
+
};
|
|
3634
|
+
child = spawn(resolvedExecutable, resolvedArgs, spawnOptions);
|
|
3635
|
+
// Start the timeout only after the OS has spawned the process so
|
|
3636
|
+
// timeoutMillis measures command runtime, not spawn/setup overhead.
|
|
3637
|
+
child.on('spawn', () => {
|
|
3638
|
+
if (settled || canceling)
|
|
3639
|
+
return;
|
|
3640
|
+
timeout = setTimeout(() => {
|
|
3641
|
+
void cancelWith('timeout', `Process timed out after ${timeoutMillis} milliseconds`);
|
|
3642
|
+
}, timeoutMillis);
|
|
3643
|
+
});
|
|
3455
3644
|
child.stdout?.on('data', (data) => { stdout += data; });
|
|
3456
3645
|
child.stderr?.on('data', (data) => { stderr += data; });
|
|
3457
3646
|
child.on('error', (err) => {
|
|
3647
|
+
if (settled || canceling)
|
|
3648
|
+
return;
|
|
3458
3649
|
finish(failure({
|
|
3459
3650
|
message: err.message,
|
|
3460
3651
|
binaryPath,
|
|
3461
3652
|
args,
|
|
3462
3653
|
stdout,
|
|
3463
3654
|
stderr,
|
|
3655
|
+
reason: 'spawn',
|
|
3464
3656
|
}));
|
|
3465
3657
|
});
|
|
3466
3658
|
child.on('close', (code) => {
|
|
3659
|
+
if (settled || canceling)
|
|
3660
|
+
return;
|
|
3467
3661
|
if (code === 0) {
|
|
3468
3662
|
finish(success({ stdout, stderr }));
|
|
3469
3663
|
return;
|
|
@@ -3475,6 +3669,7 @@ function execBinary(binaryPath, args, timeoutMillis = 1000 * 60 * 10, options) {
|
|
|
3475
3669
|
code: code ?? undefined,
|
|
3476
3670
|
stdout,
|
|
3477
3671
|
stderr,
|
|
3672
|
+
reason: 'exit',
|
|
3478
3673
|
}));
|
|
3479
3674
|
});
|
|
3480
3675
|
});
|
|
@@ -3550,7 +3745,28 @@ async function collectStepsForContext(params) {
|
|
|
3550
3745
|
return collectedSteps;
|
|
3551
3746
|
}
|
|
3552
3747
|
|
|
3553
|
-
async function
|
|
3748
|
+
async function runOptionalGitCommand(input) {
|
|
3749
|
+
const { label, getCommand, cwd, logger } = input;
|
|
3750
|
+
try {
|
|
3751
|
+
const command = await getCommand();
|
|
3752
|
+
if (command == null || command === '') {
|
|
3753
|
+
return 'ok';
|
|
3754
|
+
}
|
|
3755
|
+
const result = await execAsync(command, { cwd });
|
|
3756
|
+
logger.verbose(`${label} ${JSON.stringify(result)}`);
|
|
3757
|
+
if (!result.success) {
|
|
3758
|
+
logger.error(formatExecFailureMessage({ label, failure: result }));
|
|
3759
|
+
return 'failed';
|
|
3760
|
+
}
|
|
3761
|
+
return 'ok';
|
|
3762
|
+
}
|
|
3763
|
+
catch (error) {
|
|
3764
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3765
|
+
logger.error(`Failed to run ${label}: ${message}`);
|
|
3766
|
+
return 'failed';
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList, gitAddCommandFn, gitCommitCommandFn, gitPushCommandFn, gitCommitMessageFn, projectRoot, steps, setupFn, setupWorkspaceFn, teardownFn, teardownWorkspaceFn, getKeepHistoryFilePathFn, logger: loggerInput, signal, }) {
|
|
3554
3770
|
const logger = loggerInput ?? createConsoleLogger({});
|
|
3555
3771
|
const contextNames = contextList.map(context => context.name);
|
|
3556
3772
|
logger.verbose(`contextNames ${JSON.stringify(contextNames)}`);
|
|
@@ -3585,180 +3801,232 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
|
|
|
3585
3801
|
const gitStatusCommand = await execAsync(`git status`, { cwd: workspacePath });
|
|
3586
3802
|
logger.verbose(`gitStatusCommand ${JSON.stringify(gitStatusCommand.data)}`);
|
|
3587
3803
|
injectedGitAndWorkspaceFnsInput.workspacePath = workspacePath;
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
if (typeof step === 'function') {
|
|
3611
|
-
subSteps =
|
|
3804
|
+
let runFailure;
|
|
3805
|
+
try {
|
|
3806
|
+
for (let i = 0; i < contextList.length; i++) {
|
|
3807
|
+
const context = contextList[i];
|
|
3808
|
+
logger.info(contextList.length > 1
|
|
3809
|
+
? `Running context "${context.name}" (${i + 1}/${contextList.length})`
|
|
3810
|
+
: `Running context "${context.name}"`);
|
|
3811
|
+
const setupResult = await setupFn({
|
|
3812
|
+
contextList,
|
|
3813
|
+
lumpVariables,
|
|
3814
|
+
currentContextIndex: i,
|
|
3815
|
+
});
|
|
3816
|
+
const contextRunState = setupResult?.contextRunState || {};
|
|
3817
|
+
let stepWalkFailure;
|
|
3818
|
+
async function walkAndExecuteSteps(stepsToExec, currStepIndex) {
|
|
3819
|
+
for (let stepIndex = 0; stepIndex < stepsToExec.length; stepIndex++) {
|
|
3820
|
+
if (stepWalkFailure) {
|
|
3821
|
+
return;
|
|
3822
|
+
}
|
|
3823
|
+
const step = stepsToExec[stepIndex];
|
|
3824
|
+
const nextCallHeadIndex = [...currStepIndex, stepIndex];
|
|
3825
|
+
const compositeStepIndex = nextCallHeadIndex.length === 1 ? nextCallHeadIndex[0] : nextCallHeadIndex;
|
|
3826
|
+
if (typeof step === 'function' || Array.isArray(step)) {
|
|
3827
|
+
let subSteps = [];
|
|
3828
|
+
if (typeof step === 'function') {
|
|
3829
|
+
subSteps = await step({
|
|
3830
|
+
context,
|
|
3831
|
+
stepIndex: compositeStepIndex,
|
|
3832
|
+
contextRunState,
|
|
3833
|
+
lumpVariables,
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
else {
|
|
3837
|
+
subSteps = step;
|
|
3838
|
+
}
|
|
3839
|
+
await walkAndExecuteSteps(subSteps, nextCallHeadIndex);
|
|
3840
|
+
continue;
|
|
3841
|
+
}
|
|
3842
|
+
logger.verbose(`step ${JSON.stringify(step)}`);
|
|
3843
|
+
const { commandFn = () => null, stepVariables, promptFn, postCommandExecFn, continueOnError, timeoutMillis = 1000 * 60 * 30, } = step;
|
|
3844
|
+
const prompt = promptFn
|
|
3845
|
+
? await promptFn({
|
|
3612
3846
|
context,
|
|
3613
3847
|
stepIndex: compositeStepIndex,
|
|
3614
3848
|
contextRunState,
|
|
3615
3849
|
lumpVariables,
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
}
|
|
3621
|
-
await walkAndExecuteSteps(subSteps, nextCallHeadIndex);
|
|
3622
|
-
continue;
|
|
3623
|
-
}
|
|
3624
|
-
logger.verbose(`step ${JSON.stringify(step)}`);
|
|
3625
|
-
const { commandFn = () => null, stepVariables, promptFn, postCommandExecFn, continueOnError, timeoutMillis = 1000 * 60 * 30, } = step;
|
|
3626
|
-
const prompt = promptFn
|
|
3627
|
-
? await promptFn({
|
|
3850
|
+
stepVariables,
|
|
3851
|
+
})
|
|
3852
|
+
: '';
|
|
3853
|
+
const command = await commandFn({
|
|
3628
3854
|
context,
|
|
3855
|
+
prompt,
|
|
3629
3856
|
stepIndex: compositeStepIndex,
|
|
3630
3857
|
contextRunState,
|
|
3631
3858
|
lumpVariables,
|
|
3632
3859
|
stepVariables,
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
const command = await commandFn({
|
|
3636
|
-
context,
|
|
3637
|
-
prompt,
|
|
3638
|
-
stepIndex: compositeStepIndex,
|
|
3639
|
-
contextRunState,
|
|
3640
|
-
lumpVariables,
|
|
3641
|
-
stepVariables,
|
|
3642
|
-
projectRoot,
|
|
3643
|
-
workspacePath,
|
|
3644
|
-
});
|
|
3645
|
-
let commandResult = '';
|
|
3646
|
-
let commandSucceeded = true;
|
|
3647
|
-
if (command != null) {
|
|
3648
|
-
const { executable, args, env } = command;
|
|
3649
|
-
logger.verbose(`command for prompt ${executable} ${args.join(' ')}`);
|
|
3650
|
-
if (env != null) {
|
|
3651
|
-
logger.verbose(`command env overrides ${JSON.stringify(env)}`);
|
|
3652
|
-
}
|
|
3653
|
-
logger.verbose(`workspacePath ${workspacePath}`);
|
|
3654
|
-
const commandExec = await execBinary(executable, args, timeoutMillis, {
|
|
3655
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
3656
|
-
cwd: workspacePath,
|
|
3657
|
-
...(env != null ? { env: { ...process.env, ...env } } : {}),
|
|
3860
|
+
projectRoot,
|
|
3861
|
+
workspacePath,
|
|
3658
3862
|
});
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3863
|
+
let commandResult = '';
|
|
3864
|
+
let commandSucceeded = true;
|
|
3865
|
+
if (command != null) {
|
|
3866
|
+
const { executable, args, env } = command;
|
|
3867
|
+
logger.verbose(`command for prompt ${executable} ${args.join(' ')}`);
|
|
3868
|
+
if (env != null) {
|
|
3869
|
+
logger.verbose(`command env overrides ${JSON.stringify(env)}`);
|
|
3870
|
+
}
|
|
3871
|
+
logger.verbose(`workspacePath ${workspacePath}`);
|
|
3872
|
+
const commandExec = await execBinary({
|
|
3873
|
+
binaryPath: executable,
|
|
3874
|
+
args,
|
|
3875
|
+
timeoutMillis,
|
|
3876
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
3877
|
+
cwd: workspacePath,
|
|
3878
|
+
signal,
|
|
3879
|
+
...(env != null ? { env: { ...process.env, ...env } } : {}),
|
|
3880
|
+
});
|
|
3881
|
+
logger.verbose(`commandExec ${JSON.stringify(commandExec)}`);
|
|
3882
|
+
if (!commandExec.success) {
|
|
3883
|
+
const aborted = commandExec.data.reason === 'aborted';
|
|
3884
|
+
if (aborted || !continueOnError) {
|
|
3885
|
+
stepWalkFailure = {
|
|
3886
|
+
success: false,
|
|
3887
|
+
data: {
|
|
3888
|
+
message: `Failed to run the command: ${commandExec.data.message}. Command: ${executable} ${args.join(' ')}`,
|
|
3889
|
+
reason: 'stepWalkFailed',
|
|
3890
|
+
},
|
|
3891
|
+
};
|
|
3892
|
+
return;
|
|
3893
|
+
}
|
|
3894
|
+
commandSucceeded = false;
|
|
3895
|
+
commandResult = (commandExec.data.stdout
|
|
3896
|
+
|| commandExec.data.stderr
|
|
3897
|
+
|| commandExec.data.message
|
|
3898
|
+
|| '').toString();
|
|
3899
|
+
logger.verbose(`commandResult ${commandResult}`);
|
|
3900
|
+
}
|
|
3901
|
+
else {
|
|
3902
|
+
commandResult = (commandExec.data.stdout || commandExec.data.stderr || '').toString();
|
|
3903
|
+
logger.verbose(`commandResult ${commandResult}`);
|
|
3904
|
+
}
|
|
3905
|
+
if (commandSucceeded) {
|
|
3906
|
+
const gitStatusAfterCommand = await execAsync(`git status`, { cwd: workspacePath });
|
|
3907
|
+
logger.verbose(`gitStatusCommand ${JSON.stringify(gitStatusAfterCommand.data)}`);
|
|
3664
3908
|
}
|
|
3665
|
-
commandSucceeded = false;
|
|
3666
|
-
commandResult = (commandExec.data.stdout
|
|
3667
|
-
|| commandExec.data.stderr
|
|
3668
|
-
|| commandExec.data.message
|
|
3669
|
-
|| '').toString();
|
|
3670
|
-
logger.verbose(`commandResult ${commandResult}`);
|
|
3671
3909
|
}
|
|
3672
|
-
|
|
3673
|
-
commandResult
|
|
3674
|
-
|
|
3910
|
+
const postCommandExecFnInput = {
|
|
3911
|
+
commandResult,
|
|
3912
|
+
commandSucceeded,
|
|
3913
|
+
context,
|
|
3914
|
+
prompt,
|
|
3915
|
+
stepIndex: compositeStepIndex,
|
|
3916
|
+
contextRunState,
|
|
3917
|
+
lumpVariables,
|
|
3918
|
+
stepVariables,
|
|
3919
|
+
projectRoot,
|
|
3920
|
+
};
|
|
3921
|
+
logger.verbose(`context is ${JSON.stringify(context)}`);
|
|
3922
|
+
const keepHistoryFilePath = getKeepHistoryFilePathFn(context) || '';
|
|
3923
|
+
logger.verbose(`keepHistoryFilePath ${keepHistoryFilePath}`);
|
|
3924
|
+
if (!!command && keepHistoryFilePath.length > 0) {
|
|
3925
|
+
const appendResult = await appendHistoryEntry({
|
|
3926
|
+
filePath: keepHistoryFilePath,
|
|
3927
|
+
entry: postCommandExecFnInput,
|
|
3928
|
+
});
|
|
3929
|
+
if (!appendResult.success) {
|
|
3930
|
+
// TODO: sanitize history appending to avoid this warning for certain commands outputs
|
|
3931
|
+
logger.warn(`Failed to append history entry to ${keepHistoryFilePath}: ${appendResult.data}`);
|
|
3932
|
+
}
|
|
3675
3933
|
}
|
|
3676
|
-
if (
|
|
3677
|
-
const
|
|
3678
|
-
|
|
3934
|
+
if (postCommandExecFn) {
|
|
3935
|
+
const returnedSteps = await postCommandExecFn(postCommandExecFnInput);
|
|
3936
|
+
if (returnedSteps != null && returnedSteps.length > 0) {
|
|
3937
|
+
await walkAndExecuteSteps(returnedSteps, nextCallHeadIndex);
|
|
3938
|
+
}
|
|
3679
3939
|
}
|
|
3680
3940
|
}
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
logger.verbose(`context is ${JSON.stringify(context)}`);
|
|
3693
|
-
const keepHistoryFilePath = getKeepHistoryFilePathFn(context) || '';
|
|
3694
|
-
logger.verbose(`keepHistoryFilePath ${keepHistoryFilePath}`);
|
|
3695
|
-
if (!!command && keepHistoryFilePath.length > 0) {
|
|
3696
|
-
const appendResult = await appendHistoryEntry({
|
|
3697
|
-
filePath: keepHistoryFilePath,
|
|
3698
|
-
entry: postCommandExecFnInput,
|
|
3941
|
+
}
|
|
3942
|
+
try {
|
|
3943
|
+
await walkAndExecuteSteps(steps, []);
|
|
3944
|
+
}
|
|
3945
|
+
finally {
|
|
3946
|
+
try {
|
|
3947
|
+
await teardownFn({
|
|
3948
|
+
lumpVariables,
|
|
3949
|
+
contextList,
|
|
3950
|
+
currentContextIndex: i,
|
|
3951
|
+
contextRunState,
|
|
3699
3952
|
});
|
|
3700
|
-
if (!appendResult.success) {
|
|
3701
|
-
// TODO: sanitize history appending to avoid this warning for certain commands outputs
|
|
3702
|
-
logger.warn(`Failed to append history entry to ${keepHistoryFilePath}: ${appendResult.data}`);
|
|
3703
|
-
}
|
|
3704
3953
|
}
|
|
3705
|
-
|
|
3706
|
-
const
|
|
3707
|
-
|
|
3708
|
-
await walkAndExecuteSteps(returnedSteps, nextCallHeadIndex);
|
|
3709
|
-
}
|
|
3954
|
+
catch (error) {
|
|
3955
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3956
|
+
logger.error(`Failed to run teardownFn: ${message}`);
|
|
3710
3957
|
}
|
|
3711
3958
|
}
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
commitMessage,
|
|
3736
|
-
|
|
3737
|
-
logger.verbose(`commitCommand ${JSON.stringify(commitCommand)}`);
|
|
3738
|
-
if (!commitCommand.success) {
|
|
3739
|
-
logger.error(formatExecFailureMessage({
|
|
3959
|
+
if (stepWalkFailure) {
|
|
3960
|
+
runFailure = stepWalkFailure;
|
|
3961
|
+
break;
|
|
3962
|
+
}
|
|
3963
|
+
const perContextInput = {
|
|
3964
|
+
...injectedGitAndWorkspaceFnsInput,
|
|
3965
|
+
context,
|
|
3966
|
+
};
|
|
3967
|
+
const addOutcome = await runOptionalGitCommand({
|
|
3968
|
+
label: `git add for context ${context.name}`,
|
|
3969
|
+
getCommand: () => gitAddCommandFn(perContextInput),
|
|
3970
|
+
cwd: workspacePath,
|
|
3971
|
+
logger,
|
|
3972
|
+
});
|
|
3973
|
+
if (addOutcome === 'failed') {
|
|
3974
|
+
runFailure = {
|
|
3975
|
+
success: false,
|
|
3976
|
+
data: {
|
|
3977
|
+
message: `Failed to add the changes for context ${context.name}`,
|
|
3978
|
+
},
|
|
3979
|
+
};
|
|
3980
|
+
break;
|
|
3981
|
+
}
|
|
3982
|
+
const commitMessage = gitCommitMessageFn({ context, lumpVariables, baseBranch });
|
|
3983
|
+
await runOptionalGitCommand({
|
|
3740
3984
|
label: `git commit for context ${context.name}`,
|
|
3741
|
-
|
|
3742
|
-
|
|
3985
|
+
getCommand: () => gitCommitCommandFn({
|
|
3986
|
+
...perContextInput,
|
|
3987
|
+
commitMessage,
|
|
3988
|
+
}),
|
|
3989
|
+
cwd: workspacePath,
|
|
3990
|
+
logger,
|
|
3991
|
+
});
|
|
3992
|
+
}
|
|
3993
|
+
if (!runFailure) {
|
|
3994
|
+
await runOptionalGitCommand({
|
|
3995
|
+
label: `git push on branch ${branchName}`,
|
|
3996
|
+
getCommand: () => gitPushCommandFn(injectedGitAndWorkspaceFnsInput),
|
|
3997
|
+
cwd: workspacePath,
|
|
3998
|
+
logger,
|
|
3999
|
+
});
|
|
3743
4000
|
}
|
|
3744
4001
|
}
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
4002
|
+
finally {
|
|
4003
|
+
const teardownWorkspaceCommand = await teardownWorkspaceFn(injectedGitAndWorkspaceFnsInput);
|
|
4004
|
+
logger.verbose(`teardownWorkspaceCommand ${teardownWorkspaceCommand}`);
|
|
4005
|
+
if (teardownWorkspaceCommand) {
|
|
4006
|
+
const teardownWorkspaceCommandExec = await execAsync(teardownWorkspaceCommand, { cwd: workspacePath });
|
|
4007
|
+
logger.verbose(`teardownWorkspaceCommandExec ${JSON.stringify(teardownWorkspaceCommandExec)}`);
|
|
4008
|
+
if (!teardownWorkspaceCommandExec.success) {
|
|
4009
|
+
if (runFailure) {
|
|
4010
|
+
logger.error(formatExecFailureMessage({
|
|
4011
|
+
label: 'teardown workspace',
|
|
4012
|
+
failure: teardownWorkspaceCommandExec,
|
|
4013
|
+
}));
|
|
4014
|
+
}
|
|
4015
|
+
else {
|
|
4016
|
+
runFailure = {
|
|
4017
|
+
success: false,
|
|
4018
|
+
data: {
|
|
4019
|
+
message: `Failed to teardown the workspace: ${teardownWorkspaceCommandExec.data.message}`,
|
|
4020
|
+
reason: 'workspaceTeardownFailed',
|
|
4021
|
+
},
|
|
4022
|
+
};
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
3760
4025
|
}
|
|
3761
4026
|
}
|
|
4027
|
+
if (runFailure) {
|
|
4028
|
+
return runFailure;
|
|
4029
|
+
}
|
|
3762
4030
|
return success({
|
|
3763
4031
|
branchName,
|
|
3764
4032
|
contextNames,
|
|
@@ -3844,17 +4112,32 @@ async function scanDirectory({ dirPath, allPaths, gitignoresMap, projectRoot, lo
|
|
|
3844
4112
|
}
|
|
3845
4113
|
}
|
|
3846
4114
|
|
|
4115
|
+
/**
|
|
4116
|
+
* Refreshes remote-tracking refs for status / planning (one network fetch).
|
|
4117
|
+
* Uses `--no-write-fetch-head` so concurrent preflight/pull paths are safer.
|
|
4118
|
+
*/
|
|
4119
|
+
const refreshRemoteTrackingRefs = async (input) => {
|
|
4120
|
+
const { projectRoot, remoteName = 'origin' } = input;
|
|
4121
|
+
const result = await execAsync(`git fetch --prune --no-write-fetch-head ${remoteName}`, { cwd: projectRoot });
|
|
4122
|
+
if (!result.success) {
|
|
4123
|
+
return failure(result.data.message);
|
|
4124
|
+
}
|
|
4125
|
+
return success(undefined);
|
|
4126
|
+
};
|
|
4127
|
+
|
|
3847
4128
|
async function getContextStatus(params) {
|
|
3848
|
-
const { contextName, gitCommitMessageFn, projectRoot, baseBranch, lumpVariables: lumpVariablesInput, contextVariables = {}, remoteName = "origin", logger, } = params;
|
|
4129
|
+
const { contextName, gitCommitMessageFn, projectRoot, baseBranch, lumpVariables: lumpVariablesInput, contextVariables = {}, remoteName = "origin", logger, skipFetch = false, } = params;
|
|
3849
4130
|
const lumpVariables = (lumpVariablesInput ?? {});
|
|
3850
4131
|
const commitMessage = gitCommitMessageFn({
|
|
3851
4132
|
context: { name: contextName, variables: contextVariables },
|
|
3852
4133
|
lumpVariables,
|
|
3853
4134
|
baseBranch,
|
|
3854
4135
|
});
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
4136
|
+
if (!skipFetch) {
|
|
4137
|
+
const fetchResult = await refreshRemoteTrackingRefs({ projectRoot, remoteName });
|
|
4138
|
+
if (!fetchResult.success)
|
|
4139
|
+
return 'toDo';
|
|
4140
|
+
}
|
|
3858
4141
|
const logResult = await execAsync(`git log --remotes=${remoteName} -F --grep=${shellSingleQuote(commitMessage)} --format=${shellSingleQuote('%H %s')}`, { cwd: projectRoot });
|
|
3859
4142
|
if (!logResult.success)
|
|
3860
4143
|
return 'toDo';
|
|
@@ -3863,6 +4146,7 @@ async function getContextStatus(params) {
|
|
|
3863
4146
|
const matchingHashes = parseGitLogHashSubjectLines(logResultOutput)
|
|
3864
4147
|
.filter((entry) => entry.subject === commitMessage)
|
|
3865
4148
|
.map((entry) => entry.hash);
|
|
4149
|
+
logger?.verbose(`contextName ${contextName}`);
|
|
3866
4150
|
logger?.verbose(`remoteName ${remoteName}`);
|
|
3867
4151
|
logger?.verbose(`baseBranch ${baseBranch}`);
|
|
3868
4152
|
logger?.verbose(`commitMessage ${commitMessage}`);
|
|
@@ -3917,7 +4201,7 @@ function validateContextListNames(contextList) {
|
|
|
3917
4201
|
}
|
|
3918
4202
|
|
|
3919
4203
|
async function getToDoContextList(params) {
|
|
3920
|
-
const { getContextListFn, lumpVariables, gitCommitMessageFn, projectRoot, baseBranch, logger } = params;
|
|
4204
|
+
const { getContextListFn, lumpVariables, gitCommitMessageFn, projectRoot, baseBranch, logger, refreshRemoteTrackingRefsFn = refreshRemoteTrackingRefs, } = params;
|
|
3921
4205
|
const codeBasePathsResult = await getCodeBasePaths({ cwd: projectRoot, logger });
|
|
3922
4206
|
if (!codeBasePathsResult.success) {
|
|
3923
4207
|
return failure({
|
|
@@ -3935,21 +4219,28 @@ async function getToDoContextList(params) {
|
|
|
3935
4219
|
const allCtxNames = contextList.flatMap(context => [context.name, ...(context.options?.dependsOnContexts ?? [])]);
|
|
3936
4220
|
const allCtxNamesSet = new Set(allCtxNames);
|
|
3937
4221
|
const allCtxNamesList = Array.from(allCtxNamesSet);
|
|
3938
|
-
const
|
|
3939
|
-
|
|
3940
|
-
|
|
4222
|
+
const refreshResult = await refreshRemoteTrackingRefsFn({ projectRoot });
|
|
4223
|
+
let contextStatusMap;
|
|
4224
|
+
if (!refreshResult.success) {
|
|
4225
|
+
logger?.warn(`Failed to refresh remote-tracking refs for context status; treating contexts as toDo: ${refreshResult.data}`);
|
|
4226
|
+
contextStatusMap = new Map(allCtxNamesList.map((name) => [name, 'toDo']));
|
|
4227
|
+
}
|
|
4228
|
+
else {
|
|
4229
|
+
const contextStatusList = await Promise.all(allCtxNamesList.map((contextName) => getContextStatus({
|
|
4230
|
+
contextName,
|
|
3941
4231
|
contextVariables: {}, // TODO: Remove contextVariables from getContextStatus, really not needed
|
|
3942
4232
|
gitCommitMessageFn,
|
|
3943
4233
|
lumpVariables,
|
|
3944
4234
|
projectRoot,
|
|
3945
4235
|
baseBranch,
|
|
3946
4236
|
logger,
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
4237
|
+
skipFetch: true,
|
|
4238
|
+
})));
|
|
4239
|
+
contextStatusMap = new Map(allCtxNamesList.map((contextName, i) => [contextName, contextStatusList[i]]));
|
|
4240
|
+
}
|
|
3950
4241
|
const contextListToDo = contextList
|
|
3951
|
-
.filter((context
|
|
3952
|
-
const contextStatus =
|
|
4242
|
+
.filter((context) => {
|
|
4243
|
+
const contextStatus = contextStatusMap.get(context.name);
|
|
3953
4244
|
if (contextStatus && contextStatus !== 'toDo')
|
|
3954
4245
|
return false;
|
|
3955
4246
|
const deps = context.options?.dependsOnContexts;
|
|
@@ -4015,7 +4306,7 @@ const defaultSetupWorkspaceFnWithWorktree = async (input) => {
|
|
|
4015
4306
|
};
|
|
4016
4307
|
|
|
4017
4308
|
async function runLump(input) {
|
|
4018
|
-
const { baseBranch, branchFn, lumpVariables: lumpVariablesInput, getContextListFn, gitAddCommandFn = defaultGitAddCommandFn, gitCommitCommandFn = defaultGitCommitCommandFn, gitCommitMessageFn = defaultGitCommitMessageFn, gitPushCommandFn = defaultGitPushCommandFn, numberOfContextsPerBranch = 1, projectRoot, steps, setupFn = () => ({ contextRunState: {} }), setupWorkspaceFn = defaultSetupWorkspaceFn, teardownFn = () => undefined, teardownWorkspaceFn = defaultTeardownWorkspaceFn, getKeepHistoryFilePathFn = () => undefined, logger: loggerInput, } = input;
|
|
4309
|
+
const { baseBranch, branchFn, lumpVariables: lumpVariablesInput, getContextListFn, gitAddCommandFn = defaultGitAddCommandFn, gitCommitCommandFn = defaultGitCommitCommandFn, gitCommitMessageFn = defaultGitCommitMessageFn, gitPushCommandFn = defaultGitPushCommandFn, numberOfContextsPerBranch = 1, projectRoot, steps, setupFn = () => ({ contextRunState: {} }), setupWorkspaceFn = defaultSetupWorkspaceFn, teardownFn = () => undefined, teardownWorkspaceFn = defaultTeardownWorkspaceFn, getKeepHistoryFilePathFn = () => undefined, logger: loggerInput, signal, refreshRemoteTrackingRefsFn, } = input;
|
|
4019
4310
|
const lumpVariables = (lumpVariablesInput ?? {});
|
|
4020
4311
|
const logger = loggerInput ?? createConsoleLogger({});
|
|
4021
4312
|
const contextListToDoResult = await getToDoContextList({
|
|
@@ -4025,6 +4316,7 @@ async function runLump(input) {
|
|
|
4025
4316
|
baseBranch,
|
|
4026
4317
|
gitCommitMessageFn,
|
|
4027
4318
|
logger,
|
|
4319
|
+
refreshRemoteTrackingRefsFn,
|
|
4028
4320
|
});
|
|
4029
4321
|
if (!contextListToDoResult.success) {
|
|
4030
4322
|
return set(contextListToDoResult, ['data', 'message'], "Error in runLump: Failed to get to do context list. Original Error: " + contextListToDoResult.data.message);
|
|
@@ -4061,6 +4353,7 @@ async function runLump(input) {
|
|
|
4061
4353
|
teardownWorkspaceFn,
|
|
4062
4354
|
logger,
|
|
4063
4355
|
getKeepHistoryFilePathFn,
|
|
4356
|
+
signal,
|
|
4064
4357
|
});
|
|
4065
4358
|
if (!executeStepsResult.success) {
|
|
4066
4359
|
return set(executeStepsResult, ['data', 'message'], "Error in runLump: Failed to execute steps for context list. Original Error: " + executeStepsResult.data.message);
|
|
@@ -4070,5 +4363,5 @@ async function runLump(input) {
|
|
|
4070
4363
|
});
|
|
4071
4364
|
}
|
|
4072
4365
|
|
|
4073
|
-
export { appendHistoryEntry, collectStepsForContext, contextStatus, createConsoleLogger, defaultGitAddCommandFn, defaultGitCommitCommandFn, defaultGitCommitMessageFn, defaultGitPushCommandFn, defaultSetupWorkspaceFn, defaultSetupWorkspaceFnWithWorktree, defaultTeardownWorkspaceFn, defaultTeardownWorkspaceFnWithWorktree, execAsync, execBinary, executeStepsForContextList, failure, formatExecFailureMessage, getCodeBasePaths, getContextStatus, getToDoContextList, historyFormatFromPath, parseGitLogHashSubjectLines, pathExists, readHistoryFile, resolveSpawnExecutable, runLump, set, shellSingleQuote, success, validateContextListNames, windowsNpmCmdShimBody, writeHistoryFile };
|
|
4366
|
+
export { appendHistoryEntry, collectStepsForContext, contextStatus, createConsoleLogger, defaultGitAddCommandFn, defaultGitCommitCommandFn, defaultGitCommitMessageFn, defaultGitPushCommandFn, defaultSetupWorkspaceFn, defaultSetupWorkspaceFnWithWorktree, defaultTeardownWorkspaceFn, defaultTeardownWorkspaceFnWithWorktree, execAsync, execBinary, executeStepsForContextList, failure, formatExecFailureMessage, getCodeBasePaths, getContextStatus, getToDoContextList, historyFormatFromPath, isProcessAlive, killProcessTree, nodeErrnoCode, parseGitLogHashSubjectLines, pathExists, readHistoryFile, refreshRemoteTrackingRefs, resolveSpawnExecutable, runLump, set, shellSingleQuote, success, validateContextListNames, windowsNpmCmdShimBody, writeHistoryFile };
|
|
4074
4367
|
//# sourceMappingURL=index.js.map
|