@biffo/cli 0.167.0 → 0.167.1
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/index.js +41 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -948,6 +948,27 @@ var GitAdapter = class {
|
|
|
948
948
|
async fetchPrune(cwd, remote = "origin") {
|
|
949
949
|
await execa2("git", ["fetch", "--quiet", "--prune", remote], { cwd, reject: false });
|
|
950
950
|
}
|
|
951
|
+
/**
|
|
952
|
+
* Is `cwd` the primary checkout, rather than a linked worktree?
|
|
953
|
+
*
|
|
954
|
+
* The distinction decides whether being off the integration branch is a
|
|
955
|
+
* defect or the mandated state: AGENTS.md §1 requires all work to happen in a
|
|
956
|
+
* worktree on its own branch, while §2 requires the primary to stay on `dev`.
|
|
957
|
+
* Reporting the former as a problem is a false positive in the one place
|
|
958
|
+
* everybody works.
|
|
959
|
+
*
|
|
960
|
+
* A linked worktree's git dir points inside `.git/worktrees/<name>`, while the
|
|
961
|
+
* common dir is the shared `.git`. They are equal only in the primary.
|
|
962
|
+
*/
|
|
963
|
+
async isPrimaryWorktree(cwd) {
|
|
964
|
+
const opts = { cwd, reject: false };
|
|
965
|
+
const [dir, common] = await Promise.all([
|
|
966
|
+
execa2("git", ["rev-parse", "--absolute-git-dir"], opts),
|
|
967
|
+
execa2("git", ["rev-parse", "--path-format=absolute", "--git-common-dir"], opts)
|
|
968
|
+
]);
|
|
969
|
+
if (dir.exitCode !== 0 || common.exitCode !== 0) return true;
|
|
970
|
+
return dir.stdout.trim() === common.stdout.trim();
|
|
971
|
+
}
|
|
951
972
|
/**
|
|
952
973
|
* Worktrees other than the primary, with the branch each is on (#797).
|
|
953
974
|
*
|
|
@@ -9131,7 +9152,7 @@ function checkCheckoutCurrency(facts) {
|
|
|
9131
9152
|
});
|
|
9132
9153
|
return findings;
|
|
9133
9154
|
}
|
|
9134
|
-
if (facts.currentBranch !== facts.integrationBranch) {
|
|
9155
|
+
if (facts.isPrimary && facts.currentBranch !== facts.integrationBranch) {
|
|
9135
9156
|
findings.push({
|
|
9136
9157
|
check: "checkout-off-integration",
|
|
9137
9158
|
severity: "error",
|
|
@@ -9139,12 +9160,24 @@ function checkCheckoutCurrency(facts) {
|
|
|
9139
9160
|
remedy: `git switch ${facts.integrationBranch} && git pull (do the work in a worktree instead)`
|
|
9140
9161
|
});
|
|
9141
9162
|
}
|
|
9163
|
+
if (facts.isPrimary && facts.isDirty) {
|
|
9164
|
+
findings.push({
|
|
9165
|
+
check: "checkout-dirty",
|
|
9166
|
+
severity: "warn",
|
|
9167
|
+
detail: "The primary checkout has uncommitted changes, so what it contains is neither the integration branch nor anything reviewed. Editing the primary directly is what AGENTS.md \xA71 exists to prevent; work belongs in a worktree.",
|
|
9168
|
+
remedy: 'git stash push -m "<what this is>" or commit it on a branch, then work in a worktree'
|
|
9169
|
+
});
|
|
9170
|
+
}
|
|
9142
9171
|
if (facts.hasUpstream && facts.behind > 0) {
|
|
9143
9172
|
const diverged = facts.ahead > 0 ? `, and ${String(facts.ahead)} ahead (diverged)` : "";
|
|
9173
|
+
const where = facts.isPrimary ? "The primary checkout" : "This worktree";
|
|
9144
9174
|
findings.push({
|
|
9145
9175
|
check: "checkout-behind",
|
|
9146
|
-
|
|
9147
|
-
|
|
9176
|
+
// In a worktree, behind-its-own-upstream means someone else pushed to the
|
|
9177
|
+
// branch — worth knowing, but not a reason to distrust everything read
|
|
9178
|
+
// from it the way a stale primary is.
|
|
9179
|
+
severity: facts.isPrimary ? "error" : "warn",
|
|
9180
|
+
detail: `${where} is ${String(facts.behind)} commit(s) behind its upstream${diverged}. Every file read from it may be stale, including the ones that look like authoritative state.`,
|
|
9148
9181
|
remedy: "git pull --ff-only"
|
|
9149
9182
|
});
|
|
9150
9183
|
}
|
|
@@ -9153,6 +9186,7 @@ function checkCheckoutCurrency(facts) {
|
|
|
9153
9186
|
function checkCoreVersionCurrency(facts) {
|
|
9154
9187
|
if (facts.localCoreVersion === null || facts.remoteCoreVersion === null) return [];
|
|
9155
9188
|
if (facts.localCoreVersion === facts.remoteCoreVersion) return [];
|
|
9189
|
+
if (!facts.isPrimary) return [];
|
|
9156
9190
|
return [
|
|
9157
9191
|
{
|
|
9158
9192
|
check: "core-version-stale",
|
|
@@ -9247,6 +9281,8 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
|
|
|
9247
9281
|
}
|
|
9248
9282
|
if (options.fetch) await git.fetchPrune(options.cwd);
|
|
9249
9283
|
const currentBranch = await git.currentBranch(options.cwd);
|
|
9284
|
+
const isPrimary = await git.isPrimaryWorktree(options.cwd);
|
|
9285
|
+
const isDirty = await git.hasUncommittedChanges(options.cwd);
|
|
9250
9286
|
const { ahead, behind, hasUpstream } = await git.aheadBehind(options.cwd);
|
|
9251
9287
|
const branches = await git.listBranchRefs(options.cwd);
|
|
9252
9288
|
const worktreePaths = await git.listWorktrees(options.cwd);
|
|
@@ -9258,10 +9294,12 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
|
|
|
9258
9294
|
);
|
|
9259
9295
|
const facts = {
|
|
9260
9296
|
currentBranch,
|
|
9297
|
+
isPrimary,
|
|
9261
9298
|
integrationBranch: INTEGRATION_BRANCH,
|
|
9262
9299
|
ahead,
|
|
9263
9300
|
behind,
|
|
9264
9301
|
hasUpstream,
|
|
9302
|
+
isDirty,
|
|
9265
9303
|
localCoreVersion: readLocalCoreVersion(options.cwd),
|
|
9266
9304
|
remoteCoreVersion: parseCoreRecord(
|
|
9267
9305
|
await git.showFileAtRef(options.cwd, `origin/${INTEGRATION_BRANCH}`, INSTANCE_CORE_FILE)
|