audit-tools 0.32.15 → 0.32.16
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/audit/cli/nextStepHelpers.d.ts.map +1 -1
- package/dist/audit/cli/nextStepHelpers.js +5 -29
- package/dist/audit/cli/nextStepHelpers.js.map +1 -1
- package/dist/audit/cli/providerConfirmationStep.d.ts.map +1 -1
- package/dist/audit/cli/providerConfirmationStep.js +7 -2
- package/dist/audit/cli/providerConfirmationStep.js.map +1 -1
- package/dist/audit/orchestrator/runtimeCommand.d.ts.map +1 -1
- package/dist/audit/orchestrator/runtimeCommand.js +1 -0
- package/dist/audit/orchestrator/runtimeCommand.js.map +1 -1
- package/dist/audit/validation/sessionConfig.d.ts.map +1 -1
- package/dist/audit/validation/sessionConfig.js +1 -0
- package/dist/audit/validation/sessionConfig.js.map +1 -1
- package/dist/remediate/steps/dispatch.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch.js +34 -35
- package/dist/remediate/steps/dispatch.js.map +1 -1
- package/dist/remediate/steps/rollingSession.d.ts.map +1 -1
- package/dist/remediate/steps/rollingSession.js +4 -5
- package/dist/remediate/steps/rollingSession.js.map +1 -1
- package/dist/remediate/utils/commands.d.ts.map +1 -1
- package/dist/remediate/utils/commands.js +3 -0
- package/dist/remediate/utils/commands.js.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.d.ts.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.js +2 -0
- package/dist/remediate/validation/contractPipelineGates.js.map +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/providers/providerPathGuard.d.ts.map +1 -1
- package/dist/shared/providers/providerPathGuard.js +7 -1
- package/dist/shared/providers/providerPathGuard.js.map +1 -1
- package/dist/shared/providers/spawnLoggedCommand.d.ts.map +1 -1
- package/dist/shared/providers/spawnLoggedCommand.js +4 -0
- package/dist/shared/providers/spawnLoggedCommand.js.map +1 -1
- package/dist/shared/quota/antigravityQuotaSource.js +1 -1
- package/dist/shared/quota/antigravityQuotaSource.js.map +1 -1
- package/dist/shared/tooling/exec.d.ts +12 -1
- package/dist/shared/tooling/exec.d.ts.map +1 -1
- package/dist/shared/tooling/exec.js +14 -0
- package/dist/shared/tooling/exec.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,10 +6,9 @@ import { routeAmendmentRequest } from "../dispatch/amendmentClaim.js";
|
|
|
6
6
|
import { toBlockId, fromBlockId } from "../contractPipeline/idRegistry.js";
|
|
7
7
|
import { readContractArtifact } from "../contractPipeline/artifactStore.js";
|
|
8
8
|
import { verifyPairingForFinding } from "../contractPipeline/changeClassification.js";
|
|
9
|
-
import { spawnSync } from "node:child_process";
|
|
10
9
|
import { StateStore } from "../state/store.js";
|
|
11
10
|
import { REMEDIATION_STEP, isClarificationCategory, } from "../state/types.js";
|
|
12
|
-
import { computeDispatchAdmission, createReservationLedger, tierRank } from "audit-tools/shared";
|
|
11
|
+
import { computeDispatchAdmission, createReservationLedger, tierRank, spawnSyncHidden } from "audit-tools/shared";
|
|
13
12
|
import { deriveCostRank, lookupConfirmedPosition, readConfirmedCostPositions } from "audit-tools/shared";
|
|
14
13
|
import { scheduleWave as computePoolWaveSchedule } from "audit-tools/shared";
|
|
15
14
|
import { withFileLock } from "audit-tools/shared";
|
|
@@ -412,7 +411,7 @@ function getCachedConventions(root) {
|
|
|
412
411
|
* --show-toplevel` emits a forward-slash absolute path on every platform.
|
|
413
412
|
*/
|
|
414
413
|
export function gitTopLevel(cwd) {
|
|
415
|
-
const result =
|
|
414
|
+
const result = spawnSyncHidden("git", ["rev-parse", "--show-toplevel"], {
|
|
416
415
|
cwd,
|
|
417
416
|
encoding: "utf8",
|
|
418
417
|
shell: false,
|
|
@@ -456,7 +455,7 @@ export function createWorktree(root, worktreePath, branchName) {
|
|
|
456
455
|
`itself, so 'git worktree add' would escape to an ancestor repo. Initialize a git repo at the ` +
|
|
457
456
|
`target root before rolling dispatch.`);
|
|
458
457
|
}
|
|
459
|
-
const result =
|
|
458
|
+
const result = spawnSyncHidden("git", ["worktree", "add", "-b", branchName, worktreePath, "HEAD"], { cwd: root, encoding: "utf8", shell: false });
|
|
460
459
|
if (result.status !== 0) {
|
|
461
460
|
const stderr = (result.stderr ?? "").trim();
|
|
462
461
|
const stdout = (result.stdout ?? "").trim();
|
|
@@ -509,7 +508,7 @@ export function removeWorktree(root, worktreePath) {
|
|
|
509
508
|
// exactly as before. We do NOT match on the 'not a working tree' stderr text.
|
|
510
509
|
if (!existsSync(worktreePath))
|
|
511
510
|
return;
|
|
512
|
-
const result =
|
|
511
|
+
const result = spawnSyncHidden("git", ["worktree", "remove", "--force", worktreePath], { cwd: root, encoding: "utf8", shell: false });
|
|
513
512
|
if (result.status !== 0) {
|
|
514
513
|
const stderr = (result.stderr ?? "").trim();
|
|
515
514
|
process.stderr.write(`[remediate-code] worktree remove failed (exit ${result.status ?? "unknown"}): ${stderr}\n`);
|
|
@@ -532,9 +531,9 @@ export function resetNodeWorktreeAndBranch(root, worktreePath, branchName) {
|
|
|
532
531
|
removeWorktree(root, worktreePath);
|
|
533
532
|
// Prune stale worktree admin entries (e.g. a dir deleted out from under git),
|
|
534
533
|
// otherwise `git worktree add` can refuse a path it still thinks is registered.
|
|
535
|
-
|
|
534
|
+
spawnSyncHidden("git", ["worktree", "prune"], { cwd: root, shell: false });
|
|
536
535
|
// Force-delete the leftover branch from a prior attempt so `-b` recreates it.
|
|
537
|
-
|
|
536
|
+
spawnSyncHidden("git", ["branch", "-D", branchName], { cwd: root, shell: false });
|
|
538
537
|
// Force-remove a leftover worktree DIRECTORY: when a prior attempt's worktree
|
|
539
538
|
// became an orphaned dir (registered admin entry gone but files remain),
|
|
540
539
|
// `git worktree remove` no-ops ("is not a working tree") and `git worktree add`
|
|
@@ -584,7 +583,7 @@ export function verifyNodeInWorktree(worktreePath, targetedCommands) {
|
|
|
584
583
|
/** Merge the worktree branch into the current HEAD via cherry-pick. On failure, removes the worktree and returns the error. */
|
|
585
584
|
export function mergeWorktree(root, worktreePath, branchName) {
|
|
586
585
|
// Get the tip commit of the worktree branch
|
|
587
|
-
const revResult =
|
|
586
|
+
const revResult = spawnSyncHidden("git", ["rev-parse", branchName], {
|
|
588
587
|
cwd: root,
|
|
589
588
|
encoding: "utf8",
|
|
590
589
|
shell: false,
|
|
@@ -595,7 +594,7 @@ export function mergeWorktree(root, worktreePath, branchName) {
|
|
|
595
594
|
return { success: false, error: `Failed to resolve worktree branch ${branchName}: ${errMsg}` };
|
|
596
595
|
}
|
|
597
596
|
const worktreeTip = revResult.stdout.trim();
|
|
598
|
-
const mergeResult =
|
|
597
|
+
const mergeResult = spawnSyncHidden("git", ["cherry-pick", worktreeTip], {
|
|
599
598
|
cwd: root,
|
|
600
599
|
encoding: "utf8",
|
|
601
600
|
shell: false,
|
|
@@ -603,7 +602,7 @@ export function mergeWorktree(root, worktreePath, branchName) {
|
|
|
603
602
|
if (mergeResult.status !== 0) {
|
|
604
603
|
const stderr = (mergeResult.stderr ?? "").trim();
|
|
605
604
|
// Abort the cherry-pick so the main tree stays clean
|
|
606
|
-
|
|
605
|
+
spawnSyncHidden("git", ["cherry-pick", "--abort"], { cwd: root, shell: false });
|
|
607
606
|
removeWorktree(root, worktreePath);
|
|
608
607
|
return { success: false, error: `cherry-pick failed: ${stderr}` };
|
|
609
608
|
}
|
|
@@ -626,7 +625,7 @@ export function dirtyMainTreeCollisions(root, branch) {
|
|
|
626
625
|
const edited = gitEditedFilesForBranch(root, branch);
|
|
627
626
|
if (!edited.available || edited.files.size === 0)
|
|
628
627
|
return [];
|
|
629
|
-
const status =
|
|
628
|
+
const status = spawnSyncHidden("git", ["status", "--porcelain", "--", ...edited.files], { cwd: root, encoding: "utf8", shell: false });
|
|
630
629
|
if (status.error || status.status !== 0)
|
|
631
630
|
return [];
|
|
632
631
|
const dirty = [];
|
|
@@ -655,13 +654,13 @@ export function dirtyMainTreeCollisions(root, branch) {
|
|
|
655
654
|
* node's work can still be quarantined).
|
|
656
655
|
*/
|
|
657
656
|
export function rebaseBranchOntoHead(root, worktreePath, branch) {
|
|
658
|
-
const head =
|
|
657
|
+
const head = spawnSyncHidden("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8", shell: false });
|
|
659
658
|
if (head.error || head.status !== 0) {
|
|
660
659
|
const detail = (head.stderr ?? head.error?.message ?? "git rev-parse failed").toString().trim();
|
|
661
660
|
return { ok: false, error: `could not resolve remediation HEAD for rebase: ${detail}` };
|
|
662
661
|
}
|
|
663
662
|
const target = head.stdout.trim();
|
|
664
|
-
const rebase =
|
|
663
|
+
const rebase = spawnSyncHidden("git", ["rebase", target], {
|
|
665
664
|
cwd: worktreePath,
|
|
666
665
|
encoding: "utf8",
|
|
667
666
|
shell: false,
|
|
@@ -672,7 +671,7 @@ export function rebaseBranchOntoHead(root, worktreePath, branch) {
|
|
|
672
671
|
.join("\n")
|
|
673
672
|
.trim();
|
|
674
673
|
// Leave a clean tree: abort the in-progress rebase before the worktree is dropped.
|
|
675
|
-
|
|
674
|
+
spawnSyncHidden("git", ["rebase", "--abort"], { cwd: worktreePath, shell: false });
|
|
676
675
|
return {
|
|
677
676
|
ok: false,
|
|
678
677
|
error: `rebase onto the current remediation HEAD conflicted (a real seam — two ` +
|
|
@@ -729,7 +728,7 @@ export function quarantineRef(runId, blockId) {
|
|
|
729
728
|
* a manual `git cherry-pick`. Best-effort; returns the ref + commit, or null.
|
|
730
729
|
*/
|
|
731
730
|
export function quarantineFailedNodeCommit(root, branch, runId, blockId) {
|
|
732
|
-
const rev =
|
|
731
|
+
const rev = spawnSyncHidden("git", ["rev-parse", "--verify", "--quiet", `${branch}^{commit}`], {
|
|
733
732
|
cwd: root,
|
|
734
733
|
encoding: "utf8",
|
|
735
734
|
shell: false,
|
|
@@ -738,7 +737,7 @@ export function quarantineFailedNodeCommit(root, branch, runId, blockId) {
|
|
|
738
737
|
return null;
|
|
739
738
|
const commit = (rev.stdout ?? "").trim();
|
|
740
739
|
const ref = quarantineRef(runId, blockId);
|
|
741
|
-
const upd =
|
|
740
|
+
const upd = spawnSyncHidden("git", ["update-ref", ref, commit], {
|
|
742
741
|
cwd: root,
|
|
743
742
|
encoding: "utf8",
|
|
744
743
|
shell: false,
|
|
@@ -768,7 +767,7 @@ export function quarantineUncommittedWorktreeEdits(root, worktreeRoot, branch, r
|
|
|
768
767
|
// honours .gitignore, so the incidental ignored churn (node_modules/dist) and
|
|
769
768
|
// the offending generated artifact stay out — what we preserve is the worker's
|
|
770
769
|
// real source work.
|
|
771
|
-
const add =
|
|
770
|
+
const add = spawnSyncHidden("git", ["add", "-A"], {
|
|
772
771
|
cwd: worktreeRoot,
|
|
773
772
|
encoding: "utf8",
|
|
774
773
|
shell: false,
|
|
@@ -776,13 +775,13 @@ export function quarantineUncommittedWorktreeEdits(root, worktreeRoot, branch, r
|
|
|
776
775
|
if (add.status !== 0)
|
|
777
776
|
return null;
|
|
778
777
|
// Nothing staged → no uncommitted edits to preserve.
|
|
779
|
-
const staged =
|
|
778
|
+
const staged = spawnSyncHidden("git", ["diff", "--cached", "--quiet"], {
|
|
780
779
|
cwd: worktreeRoot,
|
|
781
780
|
shell: false,
|
|
782
781
|
});
|
|
783
782
|
if (staged.status === 0)
|
|
784
783
|
return null;
|
|
785
|
-
const commit =
|
|
784
|
+
const commit = spawnSyncHidden("git", ["commit", "-m", `remediate-quarantine ${blockId} (${runId}) — fail-loud preserve`], { cwd: worktreeRoot, encoding: "utf8", shell: false });
|
|
786
785
|
if (commit.status !== 0) {
|
|
787
786
|
process.stderr.write(`[remediate-code] could not preserve uncommitted edits for ${blockId}: ${(commit.stderr ?? "").trim()}\n`);
|
|
788
787
|
return null;
|
|
@@ -791,7 +790,7 @@ export function quarantineUncommittedWorktreeEdits(root, worktreeRoot, branch, r
|
|
|
791
790
|
}
|
|
792
791
|
/** Clear a node's quarantine ref (e.g. once a later re-dispatch landed successfully). Best-effort. */
|
|
793
792
|
export function clearQuarantinedCommit(root, runId, blockId) {
|
|
794
|
-
|
|
793
|
+
spawnSyncHidden("git", ["update-ref", "-d", quarantineRef(runId, blockId)], {
|
|
795
794
|
cwd: root,
|
|
796
795
|
encoding: "utf8",
|
|
797
796
|
shell: false,
|
|
@@ -800,7 +799,7 @@ export function clearQuarantinedCommit(root, runId, blockId) {
|
|
|
800
799
|
/** Quarantined failed-node commits still preserved for a run, for recovery surfacing in the report. */
|
|
801
800
|
export function listQuarantinedCommits(root, runId) {
|
|
802
801
|
const prefix = `refs/remediation-quarantine/${refSafeSegment(runId, "run")}/`;
|
|
803
|
-
const res =
|
|
802
|
+
const res = spawnSyncHidden("git", ["for-each-ref", "--format=%(refname) %(objectname)", prefix], {
|
|
804
803
|
cwd: root,
|
|
805
804
|
encoding: "utf8",
|
|
806
805
|
shell: false,
|
|
@@ -851,7 +850,7 @@ export function ensureRemediationBranchCheckedOut(root, runId, artifactsDir) {
|
|
|
851
850
|
if (top === null || canonicalPathKey(top) !== canonicalPathKey(root))
|
|
852
851
|
return null;
|
|
853
852
|
const branch = remediationBranchName(runId);
|
|
854
|
-
const current =
|
|
853
|
+
const current = spawnSyncHidden("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
855
854
|
cwd: root,
|
|
856
855
|
encoding: "utf8",
|
|
857
856
|
shell: false,
|
|
@@ -860,7 +859,7 @@ export function ensureRemediationBranchCheckedOut(root, runId, artifactsDir) {
|
|
|
860
859
|
return branch;
|
|
861
860
|
const branchExisted = gitBranchExists(root, branch);
|
|
862
861
|
const args = branchExisted ? ["checkout", branch] : ["checkout", "-b", branch];
|
|
863
|
-
const co =
|
|
862
|
+
const co = spawnSyncHidden("git", args, { cwd: root, encoding: "utf8", shell: false });
|
|
864
863
|
if (co.status !== 0) {
|
|
865
864
|
process.stderr.write(`[remediate-code] could not switch to remediation branch ${branch}: ${(co.stderr ?? "").trim()}\n`);
|
|
866
865
|
return null;
|
|
@@ -980,7 +979,7 @@ function isUnderWritePaths(rel, declaredWritePaths, root) {
|
|
|
980
979
|
function forceAddNewSourceFiles(worktreeRoot, declaredWritePaths) {
|
|
981
980
|
if (declaredWritePaths === undefined)
|
|
982
981
|
return { ok: true };
|
|
983
|
-
const ls =
|
|
982
|
+
const ls = spawnSyncHidden("git", ["ls-files", "--others", "--ignored", "--exclude-standard"], { cwd: worktreeRoot, encoding: "utf8", shell: false });
|
|
984
983
|
if (ls.error || ls.status !== 0) {
|
|
985
984
|
return {
|
|
986
985
|
ok: false,
|
|
@@ -1018,7 +1017,7 @@ function forceAddNewSourceFiles(worktreeRoot, declaredWritePaths) {
|
|
|
1018
1017
|
};
|
|
1019
1018
|
}
|
|
1020
1019
|
for (const rel of toForceAdd) {
|
|
1021
|
-
const add =
|
|
1020
|
+
const add = spawnSyncHidden("git", ["add", "-f", "--", rel], {
|
|
1022
1021
|
cwd: worktreeRoot,
|
|
1023
1022
|
encoding: "utf8",
|
|
1024
1023
|
shell: false,
|
|
@@ -1037,7 +1036,7 @@ export function commitWorktree(worktreeRoot, message, declaredWritePaths) {
|
|
|
1037
1036
|
if (!forced.ok) {
|
|
1038
1037
|
return { committed: false, error: forced.error };
|
|
1039
1038
|
}
|
|
1040
|
-
const add =
|
|
1039
|
+
const add = spawnSyncHidden("git", ["add", "-A"], {
|
|
1041
1040
|
cwd: worktreeRoot,
|
|
1042
1041
|
encoding: "utf8",
|
|
1043
1042
|
shell: false,
|
|
@@ -1046,14 +1045,14 @@ export function commitWorktree(worktreeRoot, message, declaredWritePaths) {
|
|
|
1046
1045
|
return { committed: false, error: `git add failed: ${(add.stderr ?? "").trim()}` };
|
|
1047
1046
|
}
|
|
1048
1047
|
// `git diff --cached --quiet` exits 0 when nothing is staged → no worker edits.
|
|
1049
|
-
const staged =
|
|
1048
|
+
const staged = spawnSyncHidden("git", ["diff", "--cached", "--quiet"], {
|
|
1050
1049
|
cwd: worktreeRoot,
|
|
1051
1050
|
shell: false,
|
|
1052
1051
|
});
|
|
1053
1052
|
if (staged.status === 0) {
|
|
1054
1053
|
return { committed: false };
|
|
1055
1054
|
}
|
|
1056
|
-
const commit =
|
|
1055
|
+
const commit = spawnSyncHidden("git", ["commit", "-m", message], {
|
|
1057
1056
|
cwd: worktreeRoot,
|
|
1058
1057
|
encoding: "utf8",
|
|
1059
1058
|
shell: false,
|
|
@@ -1253,7 +1252,7 @@ export async function acceptNodeWorktree(params) {
|
|
|
1253
1252
|
}
|
|
1254
1253
|
// Capture the base HEAD OID BEFORE the cherry-pick so a RED merged-base check can
|
|
1255
1254
|
// roll the base back to a bit-identical state.
|
|
1256
|
-
const baseHeadBefore =
|
|
1255
|
+
const baseHeadBefore = spawnSyncHidden("git", ["rev-parse", "HEAD"], {
|
|
1257
1256
|
cwd: root,
|
|
1258
1257
|
encoding: "utf8",
|
|
1259
1258
|
shell: false,
|
|
@@ -1301,12 +1300,12 @@ export async function acceptNodeWorktree(params) {
|
|
|
1301
1300
|
? check.error.message
|
|
1302
1301
|
: [check.stdout ?? "", check.stderr ?? ""].filter(Boolean).join("\n");
|
|
1303
1302
|
// Roll the base back to its pre-pick OID, bit-identical.
|
|
1304
|
-
|
|
1303
|
+
spawnSyncHidden("git", ["reset", "--hard", baseOid], { cwd: root, shell: false });
|
|
1305
1304
|
// Scoped clean: remove only the cherry-pick / check-emitted untracked files
|
|
1306
1305
|
// under the paths the pick touched — never a blanket `git clean` that could
|
|
1307
1306
|
// nuke unrelated untracked state.
|
|
1308
1307
|
if (pickedFiles.available && pickedFiles.files.size > 0) {
|
|
1309
|
-
|
|
1308
|
+
spawnSyncHidden("git", ["clean", "-fdq", "--", ...[...pickedFiles.files]], { cwd: root, shell: false });
|
|
1310
1309
|
}
|
|
1311
1310
|
quarantineFailedNodeCommit(root, branch, runId, blockId);
|
|
1312
1311
|
return {
|
|
@@ -2447,12 +2446,12 @@ function assertImplementWorkerResult(value, path) {
|
|
|
2447
2446
|
}
|
|
2448
2447
|
/** True when `root` is inside a git work tree (the git tool is present and it's a repo). */
|
|
2449
2448
|
function isGitWorkTree(root) {
|
|
2450
|
-
const probe =
|
|
2449
|
+
const probe = spawnSyncHidden("git", ["rev-parse", "--is-inside-work-tree"], { cwd: root, encoding: "utf8", shell: false });
|
|
2451
2450
|
return !probe.error && probe.status === 0 && /true/.test(probe.stdout ?? "");
|
|
2452
2451
|
}
|
|
2453
2452
|
/** True when `branch` resolves to a commit in the repo at `root`. */
|
|
2454
2453
|
function gitBranchExists(root, branch) {
|
|
2455
|
-
const probe =
|
|
2454
|
+
const probe = spawnSyncHidden("git", ["rev-parse", "--verify", "--quiet", `${branch}^{commit}`], { cwd: root, encoding: "utf8", shell: false });
|
|
2456
2455
|
return !probe.error && probe.status === 0;
|
|
2457
2456
|
}
|
|
2458
2457
|
/** Normalize a declared path (absolute, repo-relative, or back-slashed) to a repo-relative forward-slash string. */
|
|
@@ -2499,7 +2498,7 @@ export function gitEditedFilesForBranch(root, branch) {
|
|
|
2499
2498
|
if (!isGitWorkTree(root)) {
|
|
2500
2499
|
return { available: false, reason: "not_a_repo", error: "root is not a git work tree" };
|
|
2501
2500
|
}
|
|
2502
|
-
const diff =
|
|
2501
|
+
const diff = spawnSyncHidden("git", ["diff", "--name-only", `HEAD...${branch}`], { cwd: root, encoding: "utf8", shell: false });
|
|
2503
2502
|
if (diff.error || typeof diff.status !== "number" || diff.status !== 0) {
|
|
2504
2503
|
const detail = (diff.stderr ?? diff.error?.message ?? "git diff failed").toString().trim();
|
|
2505
2504
|
return { available: false, reason: "probe_failed", error: detail };
|
|
@@ -2527,7 +2526,7 @@ export function gitHunksForBranch(root, branch) {
|
|
|
2527
2526
|
if (!isGitWorkTree(root)) {
|
|
2528
2527
|
return { available: false, reason: "not_a_repo", error: "root is not a git work tree" };
|
|
2529
2528
|
}
|
|
2530
|
-
const diff =
|
|
2529
|
+
const diff = spawnSyncHidden("git",
|
|
2531
2530
|
// No rename detection / context noise beyond what we parse; a plain unified
|
|
2532
2531
|
// diff carries the `+++ b/<path>` and `@@ … +start,count @@` headers we need.
|
|
2533
2532
|
["diff", `HEAD...${branch}`], { cwd: root, encoding: "utf8", shell: false });
|