brainfile 0.21.0 → 0.21.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/{chunk-FOLBAGJS.mjs → chunk-MUV7XTFA.mjs} +211 -96
- package/dist/cli.mjs +324 -207
- package/dist/commands/brief.js +1 -1
- package/dist/commands/brief.js.map +1 -1
- package/dist/commands/log.d.ts.map +1 -1
- package/dist/commands/log.js +4 -1
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/sync.d.ts +3 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +23 -14
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/where.d.ts +4 -1
- package/dist/commands/where.d.ts.map +1 -1
- package/dist/commands/where.js +9 -3
- package/dist/commands/where.js.map +1 -1
- package/dist/mcp/helpers.d.ts +3 -2
- package/dist/mcp/helpers.d.ts.map +1 -1
- package/dist/mcp/helpers.js +5 -6
- package/dist/mcp/helpers.js.map +1 -1
- package/dist/mcp/tools/brief_tool.js +1 -1
- package/dist/mcp/tools/brief_tool.js.map +1 -1
- package/dist/{tui-M2KWIG2E.mjs → tui-5TFQR53I.mjs} +1 -1
- package/dist/utils/actor.d.ts +22 -0
- package/dist/utils/actor.d.ts.map +1 -0
- package/dist/utils/actor.js +200 -0
- package/dist/utils/actor.js.map +1 -0
- package/dist/utils/board-commit-hooks.d.ts.map +1 -1
- package/dist/utils/board-commit-hooks.js +3 -2
- package/dist/utils/board-commit-hooks.js.map +1 -1
- package/dist/utils/board-repo.d.ts +33 -9
- package/dist/utils/board-repo.d.ts.map +1 -1
- package/dist/utils/board-repo.js +162 -61
- package/dist/utils/board-repo.js.map +1 -1
- package/dist/utils/board-sync.d.ts +26 -11
- package/dist/utils/board-sync.d.ts.map +1 -1
- package/dist/utils/board-sync.js +141 -56
- package/dist/utils/board-sync.js.map +1 -1
- package/package.json +2 -2
|
@@ -1003,7 +1003,7 @@ var require_ansi_styles = __commonJS({
|
|
|
1003
1003
|
});
|
|
1004
1004
|
};
|
|
1005
1005
|
var colorConvert;
|
|
1006
|
-
var makeDynamicStyles = (wrap, targetSpace,
|
|
1006
|
+
var makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => {
|
|
1007
1007
|
if (colorConvert === void 0) {
|
|
1008
1008
|
colorConvert = require_color_convert();
|
|
1009
1009
|
}
|
|
@@ -1012,7 +1012,7 @@ var require_ansi_styles = __commonJS({
|
|
|
1012
1012
|
for (const [sourceSpace, suite] of Object.entries(colorConvert)) {
|
|
1013
1013
|
const name = sourceSpace === "ansi16" ? "ansi" : sourceSpace;
|
|
1014
1014
|
if (sourceSpace === targetSpace) {
|
|
1015
|
-
styles[name] = wrap(
|
|
1015
|
+
styles[name] = wrap(identity, offset);
|
|
1016
1016
|
} else if (typeof suite === "object") {
|
|
1017
1017
|
styles[name] = wrap(suite[targetSpace], offset);
|
|
1018
1018
|
}
|
|
@@ -11549,18 +11549,34 @@ import * as path2 from "path";
|
|
|
11549
11549
|
import { spawnSync } from "child_process";
|
|
11550
11550
|
var DEFAULT_BOARD_BRANCH = "brainfile";
|
|
11551
11551
|
var BOARD_BRANCH_CONFIG_KEY = "brainfile.branch";
|
|
11552
|
+
var BOARD_REF_NAMESPACE = "refs/brainfile";
|
|
11553
|
+
var BOARD_NAME_CONFIG_KEY = "brainfile.boardName";
|
|
11554
|
+
var LEGACY_REMOTE_BRANCH_CONFIG_KEY = "brainfile.remoteBranch";
|
|
11555
|
+
var DEFAULT_BOARD_NAME = "board";
|
|
11556
|
+
function remoteBoardRef(name) {
|
|
11557
|
+
return `${BOARD_REF_NAMESPACE}/${name}`;
|
|
11558
|
+
}
|
|
11559
|
+
function boardTrackingRef(remote, name) {
|
|
11560
|
+
return `${BOARD_REF_NAMESPACE}/remotes/${remote}/${name}`;
|
|
11561
|
+
}
|
|
11562
|
+
var NETWORK_TIMEOUT_MS = 8e3;
|
|
11563
|
+
var NO_PROMPT_ENV = { GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "echo", SSH_ASKPASS: "echo" };
|
|
11564
|
+
function gitNetwork(args, cwd) {
|
|
11565
|
+
return git(args, cwd, NO_PROMPT_ENV, NETWORK_TIMEOUT_MS);
|
|
11566
|
+
}
|
|
11552
11567
|
var EXCLUDE_ENTRY = ".brainfile/";
|
|
11553
11568
|
var EXCLUDE_MARKER = "# brainfile: board worktree (managed by `brainfile`)";
|
|
11554
11569
|
var FALLBACK_IDENTITY_NAME = "brainfile";
|
|
11555
11570
|
var FALLBACK_IDENTITY_EMAIL = "brainfile@localhost";
|
|
11556
11571
|
var MAX_MESSAGE_LENGTH = 72;
|
|
11557
11572
|
var BOARD_SUBDIRS = ["board", "logs"];
|
|
11558
|
-
function git(args, cwd, env) {
|
|
11573
|
+
function git(args, cwd, env, timeoutMs) {
|
|
11559
11574
|
const result = spawnSync("git", args, {
|
|
11560
11575
|
cwd,
|
|
11561
11576
|
encoding: "utf-8",
|
|
11562
11577
|
env: env ? { ...process.env, ...env } : process.env,
|
|
11563
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
11578
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
11579
|
+
...timeoutMs ? { timeout: timeoutMs, killSignal: "SIGKILL" } : {}
|
|
11564
11580
|
});
|
|
11565
11581
|
return {
|
|
11566
11582
|
ok: result.status === 0,
|
|
@@ -11625,25 +11641,75 @@ function findTrackedBoardDir(cwd) {
|
|
|
11625
11641
|
if (!hit) return null;
|
|
11626
11642
|
return fs2.existsSync(path2.join(hit.path, "brainfile.md")) ? hit.path : null;
|
|
11627
11643
|
}
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
const
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11644
|
+
var PROBE_MISS_TTL_MS = 10 * 6e4;
|
|
11645
|
+
function probeMissFile(cwd) {
|
|
11646
|
+
const common = gitCommonDir(cwd);
|
|
11647
|
+
return common ? path2.join(common, "brainfile-probe.json") : null;
|
|
11648
|
+
}
|
|
11649
|
+
function recentProbeMiss(cwd, remote) {
|
|
11650
|
+
const file = probeMissFile(cwd);
|
|
11651
|
+
if (!file) return false;
|
|
11652
|
+
try {
|
|
11653
|
+
const miss = JSON.parse(fs2.readFileSync(file, "utf-8"));
|
|
11654
|
+
return miss.remote === remote && typeof miss.at === "number" && Date.now() - miss.at < PROBE_MISS_TTL_MS;
|
|
11655
|
+
} catch {
|
|
11656
|
+
return false;
|
|
11657
|
+
}
|
|
11658
|
+
}
|
|
11659
|
+
function recordProbeMiss(cwd, remote) {
|
|
11660
|
+
const file = probeMissFile(cwd);
|
|
11661
|
+
if (!file) return;
|
|
11662
|
+
try {
|
|
11663
|
+
fs2.writeFileSync(file, JSON.stringify({ remote, at: Date.now() }), "utf-8");
|
|
11664
|
+
} catch {
|
|
11665
|
+
}
|
|
11666
|
+
}
|
|
11667
|
+
function clearProbeMiss(cwd) {
|
|
11668
|
+
const file = probeMissFile(cwd);
|
|
11669
|
+
if (file) fs2.rmSync(file, { force: true });
|
|
11670
|
+
}
|
|
11671
|
+
function listRemoteBoards(cwd, remote) {
|
|
11672
|
+
const r = gitNetwork(["ls-remote", "--quiet", remote, `${BOARD_REF_NAMESPACE}/*`], cwd);
|
|
11673
|
+
if (!r.ok) return null;
|
|
11674
|
+
const names = [];
|
|
11675
|
+
for (const line of r.stdout.split("\n")) {
|
|
11676
|
+
const ref = line.split(" ")[1] ?? "";
|
|
11677
|
+
const name = ref.slice(BOARD_REF_NAMESPACE.length + 1);
|
|
11678
|
+
if (ref.startsWith(`${BOARD_REF_NAMESPACE}/`) && name && !name.includes("/")) names.push(name);
|
|
11645
11679
|
}
|
|
11646
|
-
|
|
11680
|
+
return names;
|
|
11681
|
+
}
|
|
11682
|
+
function findBoardStart(cwd, branch, options) {
|
|
11683
|
+
if (git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], cwd).ok) return { ref: branch, remote: null };
|
|
11684
|
+
const configuredRemote = git(["config", "--get", "brainfile.remote"], cwd).stdout || null;
|
|
11685
|
+
const remote = configuredRemote ?? (git(["remote", "get-url", "origin"], cwd).ok ? "origin" : null);
|
|
11686
|
+
if (!remote) return null;
|
|
11687
|
+
const configuredName = git(["config", "--get", BOARD_NAME_CONFIG_KEY], cwd).stdout || git(["config", "--get", LEGACY_REMOTE_BRANCH_CONFIG_KEY], cwd).stdout || null;
|
|
11688
|
+
const knownName = configuredName ?? DEFAULT_BOARD_NAME;
|
|
11689
|
+
const known = boardTrackingRef(remote, knownName);
|
|
11690
|
+
if (git(["rev-parse", "--verify", "--quiet", known], cwd).ok) return { ref: known, remote, name: knownName };
|
|
11691
|
+
if (options.fresh || !recentProbeMiss(cwd, remote)) {
|
|
11692
|
+
const names = listRemoteBoards(cwd, remote);
|
|
11693
|
+
const name = names === null ? null : configuredName ? names.includes(configuredName) ? configuredName : null : names.includes(DEFAULT_BOARD_NAME) ? DEFAULT_BOARD_NAME : names.length === 1 ? names[0] : null;
|
|
11694
|
+
if (name) {
|
|
11695
|
+
const tracking = boardTrackingRef(remote, name);
|
|
11696
|
+
if (gitNetwork(["fetch", "--quiet", "--no-tags", remote, `+${remoteBoardRef(name)}:${tracking}`], cwd).ok) {
|
|
11697
|
+
clearProbeMiss(cwd);
|
|
11698
|
+
return { ref: tracking, remote, name };
|
|
11699
|
+
}
|
|
11700
|
+
}
|
|
11701
|
+
}
|
|
11702
|
+
const legacyBranch = configuredName ?? branch;
|
|
11703
|
+
const legacy = `refs/remotes/${remote}/${legacyBranch}`;
|
|
11704
|
+
if (!git(["rev-parse", "--verify", "--quiet", legacy], cwd).ok && configuredRemote && (options.fresh || !recentProbeMiss(cwd, remote))) {
|
|
11705
|
+
gitNetwork(["fetch", "--quiet", "--no-tags", remote, `+refs/heads/${legacyBranch}:${legacy}`], cwd);
|
|
11706
|
+
}
|
|
11707
|
+
if (git(["rev-parse", "--verify", "--quiet", legacy], cwd).ok) return { ref: legacy, remote };
|
|
11708
|
+
recordProbeMiss(cwd, remote);
|
|
11709
|
+
return null;
|
|
11710
|
+
}
|
|
11711
|
+
function materializeBoardWorktree(cwd, options = {}) {
|
|
11712
|
+
const branch = boardBranch(cwd);
|
|
11647
11713
|
const worktrees = listWorktrees(cwd);
|
|
11648
11714
|
if (worktrees.length === 0) return null;
|
|
11649
11715
|
if (worktrees.some((w) => w.branch === `refs/heads/${branch}`)) return null;
|
|
@@ -11657,15 +11723,21 @@ function materializeBoardWorktree(cwd) {
|
|
|
11657
11723
|
target = path2.join(main.path, ".brainfile");
|
|
11658
11724
|
}
|
|
11659
11725
|
if (fs2.existsSync(target)) return null;
|
|
11660
|
-
const
|
|
11726
|
+
const start = findBoardStart(cwd, branch, options);
|
|
11727
|
+
if (!start) return null;
|
|
11728
|
+
const r = start.remote ? git(["worktree", "add", "--quiet", "--no-track", "-b", branch, target, start.ref], cwd) : git(["worktree", "add", "--quiet", target, branch], cwd);
|
|
11661
11729
|
if (!r.ok) return null;
|
|
11662
11730
|
ensureExcludeEntry(cwd);
|
|
11663
11731
|
for (const sub of BOARD_SUBDIRS) fs2.mkdirSync(path2.join(target, sub), { recursive: true });
|
|
11664
|
-
|
|
11665
|
-
|
|
11732
|
+
if (start.remote) {
|
|
11733
|
+
if (!git(["config", "--get", "brainfile.remote"], cwd).stdout) git(["config", "brainfile.remote", start.remote], cwd);
|
|
11734
|
+
if (start.name && start.name !== DEFAULT_BOARD_NAME && !git(["config", "--get", BOARD_NAME_CONFIG_KEY], cwd).stdout) {
|
|
11735
|
+
git(["config", BOARD_NAME_CONFIG_KEY, start.name], cwd);
|
|
11736
|
+
}
|
|
11737
|
+
}
|
|
11666
11738
|
const relative3 = path2.relative(process.cwd(), target) || ".";
|
|
11667
11739
|
process.stderr.write(
|
|
11668
|
-
|
|
11740
|
+
start.remote ? `Checked out the shared board from ${start.remote} into ${relative3}/
|
|
11669
11741
|
` : `Checked out the board from the '${branch}' branch into ${relative3}/
|
|
11670
11742
|
`
|
|
11671
11743
|
);
|
|
@@ -11731,9 +11803,6 @@ function identityArgs(dotDir) {
|
|
|
11731
11803
|
if (!git(["config", "--get", "user.email"], dotDir).stdout) args.push("-c", `user.email=${FALLBACK_IDENTITY_EMAIL}`);
|
|
11732
11804
|
return args;
|
|
11733
11805
|
}
|
|
11734
|
-
function sanitizeAgent(agent) {
|
|
11735
|
-
return agent.replace(/[<>\r\n]/g, "").trim();
|
|
11736
|
-
}
|
|
11737
11806
|
var commitListeners = [];
|
|
11738
11807
|
function onBoardCommitted(listener) {
|
|
11739
11808
|
if (!commitListeners.includes(listener)) commitListeners.push(listener);
|
|
@@ -11768,17 +11837,12 @@ function commitBoard(dotDir, options) {
|
|
|
11768
11837
|
if (unmergedFiles(dotDir).length > 0) return false;
|
|
11769
11838
|
if (!git(["add", "-A"], dotDir).ok) return false;
|
|
11770
11839
|
if (!boardIsDirty(dotDir)) return false;
|
|
11771
|
-
const
|
|
11772
|
-
const
|
|
11773
|
-
|
|
11774
|
-
env.GIT_AUTHOR_NAME = agent;
|
|
11775
|
-
env.GIT_AUTHOR_EMAIL = `${agent}@brainfile.local`;
|
|
11776
|
-
}
|
|
11777
|
-
const message = oneLine(options.message);
|
|
11840
|
+
const agent = options.agent?.replace(/[[\]<>\r\n]/g, "").trim();
|
|
11841
|
+
const suffix = agent ? ` [${agent}]` : "";
|
|
11842
|
+
const message = oneLine(options.message, suffix);
|
|
11778
11843
|
const r = git(
|
|
11779
11844
|
[...identityArgs(dotDir), "-c", "commit.gpgsign=false", "commit", "--quiet", "--no-verify", "-m", message],
|
|
11780
|
-
dotDir
|
|
11781
|
-
env
|
|
11845
|
+
dotDir
|
|
11782
11846
|
);
|
|
11783
11847
|
if (r.ok) notifyCommitted(dotDir);
|
|
11784
11848
|
return r.ok;
|
|
@@ -11794,13 +11858,6 @@ function commitHandEdits(dotDir) {
|
|
|
11794
11858
|
const summary = files.length <= 3 ? files.join(", ") : `${files.length} files`;
|
|
11795
11859
|
return commitBoard(dotDir, { message: `edit: ${summary}` });
|
|
11796
11860
|
}
|
|
11797
|
-
function resolveAgentName(argv = process.argv) {
|
|
11798
|
-
if (process.env.BRAINFILE_AGENT) return process.env.BRAINFILE_AGENT;
|
|
11799
|
-
const flag = argv.indexOf("--agent");
|
|
11800
|
-
if (flag >= 0 && argv[flag + 1] && !argv[flag + 1].startsWith("-")) return argv[flag + 1];
|
|
11801
|
-
const inline = argv.find((a) => a.startsWith("--agent="));
|
|
11802
|
-
return inline ? inline.slice("--agent=".length) : null;
|
|
11803
|
-
}
|
|
11804
11861
|
function homeBoardDir() {
|
|
11805
11862
|
return path2.join(os.homedir(), ".brainfile");
|
|
11806
11863
|
}
|
|
@@ -11808,9 +11865,10 @@ function isGlobalBoardRequested(argv = process.argv) {
|
|
|
11808
11865
|
if (process.env.BRAINFILE_GLOBAL === "1") return true;
|
|
11809
11866
|
return argv.includes("-g") || argv.includes("--global");
|
|
11810
11867
|
}
|
|
11811
|
-
function oneLine(message) {
|
|
11868
|
+
function oneLine(message, suffix = "") {
|
|
11812
11869
|
const flat = message.replace(/\s+/g, " ").trim();
|
|
11813
|
-
|
|
11870
|
+
const room = Math.max(1, MAX_MESSAGE_LENGTH - suffix.length);
|
|
11871
|
+
return `${flat.length > room ? `${flat.slice(0, room - 1)}\u2026` : flat}${suffix}`;
|
|
11814
11872
|
}
|
|
11815
11873
|
function describeCommandForCommit(command) {
|
|
11816
11874
|
const names = [];
|
|
@@ -11833,7 +11891,7 @@ var exitHookInstalled = false;
|
|
|
11833
11891
|
function scheduleBoardCommit(brainfilePath, message, delayMs = 2e3) {
|
|
11834
11892
|
const dotDir = path2.dirname(path2.resolve(brainfilePath));
|
|
11835
11893
|
if (!isTrackedBoard(dotDir)) return;
|
|
11836
|
-
const entry = pendingCommits.get(dotDir) ?? { messages: []
|
|
11894
|
+
const entry = pendingCommits.get(dotDir) ?? { messages: [] };
|
|
11837
11895
|
entry.messages.push(message);
|
|
11838
11896
|
pendingCommits.set(dotDir, entry);
|
|
11839
11897
|
if (flushTimer) clearTimeout(flushTimer);
|
|
@@ -11852,7 +11910,7 @@ function flushBoardCommits() {
|
|
|
11852
11910
|
for (const [dotDir, entry] of pendingCommits) {
|
|
11853
11911
|
const [first, ...rest] = entry.messages;
|
|
11854
11912
|
const message = rest.length === 0 ? first : `${first} (+${rest.length} more)`;
|
|
11855
|
-
commitBoard(dotDir, { message
|
|
11913
|
+
commitBoard(dotDir, { message });
|
|
11856
11914
|
}
|
|
11857
11915
|
pendingCommits.clear();
|
|
11858
11916
|
}
|
|
@@ -12130,7 +12188,7 @@ import * as fs7 from "fs";
|
|
|
12130
12188
|
import * as os3 from "os";
|
|
12131
12189
|
import * as path7 from "path";
|
|
12132
12190
|
var REMOTE_CONFIG_KEY = "brainfile.remote";
|
|
12133
|
-
var
|
|
12191
|
+
var LEGACY_CHECKED_CONFIG_KEY = "brainfile.legacyBranchChecked";
|
|
12134
12192
|
var AUTOSYNC_CONFIG_KEY = "brainfile.autosync";
|
|
12135
12193
|
var URL_REMOTE_NAME = "board";
|
|
12136
12194
|
var MERGE_DRIVER_NAME = "brainfile";
|
|
@@ -12152,14 +12210,23 @@ function configSet(dotDir, key, value) {
|
|
|
12152
12210
|
function boardRemote(dotDir) {
|
|
12153
12211
|
return configGet(dotDir, REMOTE_CONFIG_KEY);
|
|
12154
12212
|
}
|
|
12155
|
-
function
|
|
12156
|
-
const configured = configGet(dotDir,
|
|
12213
|
+
function boardRemoteName(dotDir) {
|
|
12214
|
+
const configured = configGet(dotDir, BOARD_NAME_CONFIG_KEY) ?? configGet(dotDir, LEGACY_REMOTE_BRANCH_CONFIG_KEY);
|
|
12157
12215
|
if (configured) return configured;
|
|
12216
|
+
return defaultName(dotDir, DEFAULT_BOARD_NAME);
|
|
12217
|
+
}
|
|
12218
|
+
function defaultName(dotDir, inRepo) {
|
|
12158
12219
|
if (path7.resolve(dotDir) === path7.resolve(homeBoardDir())) return "home";
|
|
12159
12220
|
if (boardRepoKind(dotDir) === "standalone" && path7.basename(path7.resolve(dotDir)) === ".brainfile") {
|
|
12160
|
-
return path7.basename(path7.dirname(path7.resolve(dotDir))) ||
|
|
12221
|
+
return path7.basename(path7.dirname(path7.resolve(dotDir))) || inRepo;
|
|
12161
12222
|
}
|
|
12162
|
-
return
|
|
12223
|
+
return inRepo;
|
|
12224
|
+
}
|
|
12225
|
+
function boardRemoteRef(dotDir) {
|
|
12226
|
+
return remoteBoardRef(boardRemoteName(dotDir));
|
|
12227
|
+
}
|
|
12228
|
+
function legacyRemoteBranch(dotDir) {
|
|
12229
|
+
return configGet(dotDir, LEGACY_REMOTE_BRANCH_CONFIG_KEY) ?? defaultName(dotDir, boardBranch(dotDir));
|
|
12163
12230
|
}
|
|
12164
12231
|
function boardAutosync(dotDir) {
|
|
12165
12232
|
const value = configGet(dotDir, AUTOSYNC_CONFIG_KEY);
|
|
@@ -12176,7 +12243,7 @@ function looksLikeRemoteUrl(target) {
|
|
|
12176
12243
|
if (target.includes("/") && fs7.existsSync(target)) return true;
|
|
12177
12244
|
return false;
|
|
12178
12245
|
}
|
|
12179
|
-
function setBoardRemote(cwd, target,
|
|
12246
|
+
function setBoardRemote(cwd, target, boardName) {
|
|
12180
12247
|
const trimmed = target.trim();
|
|
12181
12248
|
if (!trimmed) throw new Error("remote is required");
|
|
12182
12249
|
let remote;
|
|
@@ -12194,13 +12261,16 @@ function setBoardRemote(cwd, target, remoteBranch) {
|
|
|
12194
12261
|
throw new Error(`'${remote}' is not a git remote here.${hint} Pass a URL to add one.`);
|
|
12195
12262
|
}
|
|
12196
12263
|
}
|
|
12264
|
+
if (boardName !== void 0 && !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(boardName)) {
|
|
12265
|
+
throw new Error(`'${boardName}' is not a usable board name: use letters, digits, '.', '_' or '-'.`);
|
|
12266
|
+
}
|
|
12197
12267
|
configSet(cwd, REMOTE_CONFIG_KEY, remote);
|
|
12198
|
-
if (
|
|
12268
|
+
if (boardName) configSet(cwd, BOARD_NAME_CONFIG_KEY, boardName);
|
|
12199
12269
|
registerMergeDriver(cwd);
|
|
12200
12270
|
return {
|
|
12201
12271
|
remote,
|
|
12202
12272
|
url: git(["remote", "get-url", remote], cwd).stdout || null,
|
|
12203
|
-
|
|
12273
|
+
remoteRef: boardRemoteRef(cwd)
|
|
12204
12274
|
};
|
|
12205
12275
|
}
|
|
12206
12276
|
function registerMergeDriver(cwd, command) {
|
|
@@ -12255,18 +12325,12 @@ function commitCount(dotDir, range) {
|
|
|
12255
12325
|
const r = git(["rev-list", "--count", range], dotDir);
|
|
12256
12326
|
return r.ok ? Number.parseInt(r.stdout, 10) || 0 : 0;
|
|
12257
12327
|
}
|
|
12258
|
-
function
|
|
12328
|
+
function identityArgs2(dotDir) {
|
|
12259
12329
|
const args = ["-c", "commit.gpgsign=false"];
|
|
12260
12330
|
if (!git(["config", "--get", "user.email"], dotDir).ok) {
|
|
12261
12331
|
args.push("-c", "user.name=brainfile", "-c", "user.email=brainfile@localhost");
|
|
12262
12332
|
}
|
|
12263
|
-
|
|
12264
|
-
const name = agent?.replace(/[\r\n<>]/g, "").trim();
|
|
12265
|
-
if (name) {
|
|
12266
|
-
env.GIT_AUTHOR_NAME = name;
|
|
12267
|
-
env.GIT_AUTHOR_EMAIL = `${name}@brainfile.local`;
|
|
12268
|
-
}
|
|
12269
|
-
return { args, env };
|
|
12333
|
+
return args;
|
|
12270
12334
|
}
|
|
12271
12335
|
function resolveCompleteVersusEdit(dotDir) {
|
|
12272
12336
|
const status = git(["status", "--porcelain", "-z"], dotDir);
|
|
@@ -12310,16 +12374,49 @@ ${entryLine}
|
|
|
12310
12374
|
}
|
|
12311
12375
|
return resolved;
|
|
12312
12376
|
}
|
|
12377
|
+
function mergeIncoming(dotDir, incoming, result) {
|
|
12378
|
+
if (git(["merge-base", "--is-ancestor", incoming, "HEAD"], dotDir).ok) return { ok: true, pulled: 0 };
|
|
12379
|
+
const before = git(["rev-parse", "HEAD"], dotDir).stdout;
|
|
12380
|
+
const id = identityArgs2(dotDir);
|
|
12381
|
+
const merge = git([...id, "merge", "--quiet", "--no-edit", incoming], dotDir);
|
|
12382
|
+
if (!merge.ok) {
|
|
12383
|
+
resolveCompleteVersusEdit(dotDir);
|
|
12384
|
+
const remaining = unmergedFiles(dotDir);
|
|
12385
|
+
if (remaining.length > 0) {
|
|
12386
|
+
return {
|
|
12387
|
+
ok: false,
|
|
12388
|
+
result: {
|
|
12389
|
+
...result,
|
|
12390
|
+
ok: false,
|
|
12391
|
+
failure: "conflict",
|
|
12392
|
+
conflicts: remaining,
|
|
12393
|
+
warning: `merge left conflicts in ${remaining.join(", ")} \u2014 resolve them in ${dotDir}, then run sync again`
|
|
12394
|
+
}
|
|
12395
|
+
};
|
|
12396
|
+
}
|
|
12397
|
+
const commit = git([...id, "commit", "--quiet", "--no-edit", "--no-verify"], dotDir);
|
|
12398
|
+
if (!commit.ok) {
|
|
12399
|
+
return { ok: false, result: { ...result, ok: false, failure: "error", warning: `merge commit failed: ${firstLine(commit.stderr)}` } };
|
|
12400
|
+
}
|
|
12401
|
+
}
|
|
12402
|
+
return { ok: true, pulled: commitCount(dotDir, `${before}..${incoming}`) };
|
|
12403
|
+
}
|
|
12404
|
+
function legacyChecked(dotDir, remote) {
|
|
12405
|
+
return configGet(dotDir, LEGACY_CHECKED_CONFIG_KEY) === remote;
|
|
12406
|
+
}
|
|
12313
12407
|
function syncBoard(dotDir, options = {}) {
|
|
12314
|
-
const pull = options.pull ?? true;
|
|
12315
12408
|
const push = options.push ?? true;
|
|
12316
12409
|
const result = { ok: true, pulled: 0, pushed: 0, conflicts: [] };
|
|
12317
12410
|
if (!isTrackedBoard(dotDir)) return { ...result, skipped: "not-tracked" };
|
|
12318
12411
|
const remote = boardRemote(dotDir);
|
|
12319
12412
|
if (!remote) return { ...result, skipped: "no-remote" };
|
|
12320
|
-
const
|
|
12413
|
+
const name = boardRemoteName(dotDir);
|
|
12414
|
+
const remoteRef = remoteBoardRef(name);
|
|
12415
|
+
const tracking = boardTrackingRef(remote, name);
|
|
12321
12416
|
result.remote = remote;
|
|
12322
|
-
result.
|
|
12417
|
+
result.remoteRef = remoteRef;
|
|
12418
|
+
const checkLegacy = !legacyChecked(dotDir, remote);
|
|
12419
|
+
const pull = (options.pull ?? true) || checkLegacy;
|
|
12323
12420
|
const stuck = unmergedFiles(dotDir);
|
|
12324
12421
|
if (stuck.length > 0) {
|
|
12325
12422
|
return finish(dotDir, { ...result, ok: false, failure: "conflict", conflicts: stuck, warning: `unresolved conflicts: ${stuck.join(", ")}` });
|
|
@@ -12328,41 +12425,38 @@ function syncBoard(dotDir, options = {}) {
|
|
|
12328
12425
|
if (ensureBoardAttributes(dotDir)) commitBoard(dotDir, { message: "chore: merge attributes" });
|
|
12329
12426
|
registerMergeDriver(dotDir);
|
|
12330
12427
|
let fetched = false;
|
|
12428
|
+
let legacy = null;
|
|
12429
|
+
let legacyAbsent = false;
|
|
12331
12430
|
if (pull) {
|
|
12332
|
-
const fetch2 = git(["fetch", "--quiet", remote,
|
|
12431
|
+
const fetch2 = git(["fetch", "--quiet", "--no-tags", remote, `+${remoteRef}:${tracking}`], dotDir);
|
|
12333
12432
|
if (fetch2.ok) {
|
|
12334
12433
|
fetched = true;
|
|
12335
12434
|
} else if (!isMissingRemoteRef(fetch2.stderr)) {
|
|
12336
12435
|
return finish(dotDir, { ...result, ok: false, failure: "offline", warning: `fetch from ${remote} failed: ${firstLine(fetch2.stderr)}` });
|
|
12436
|
+
} else {
|
|
12437
|
+
git(["update-ref", "-d", tracking], dotDir);
|
|
12337
12438
|
}
|
|
12338
|
-
if (
|
|
12339
|
-
const
|
|
12340
|
-
const
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
if (remaining.length > 0) {
|
|
12346
|
-
return finish(dotDir, {
|
|
12347
|
-
...result,
|
|
12348
|
-
ok: false,
|
|
12349
|
-
failure: "conflict",
|
|
12350
|
-
conflicts: remaining,
|
|
12351
|
-
warning: `merge left conflicts in ${remaining.join(", ")} \u2014 resolve them in ${dotDir}, then run sync again`
|
|
12352
|
-
});
|
|
12353
|
-
}
|
|
12354
|
-
const commit = git([...id.args, "commit", "--quiet", "--no-edit", "--no-verify"], dotDir, id.env);
|
|
12355
|
-
if (!commit.ok) {
|
|
12356
|
-
return finish(dotDir, { ...result, ok: false, failure: "error", warning: `merge commit failed: ${firstLine(commit.stderr)}` });
|
|
12357
|
-
}
|
|
12439
|
+
if (checkLegacy) {
|
|
12440
|
+
const branch = legacyRemoteBranch(dotDir);
|
|
12441
|
+
const old = git(["fetch", "--quiet", "--no-tags", remote, `refs/heads/${branch}`], dotDir);
|
|
12442
|
+
if (old.ok) {
|
|
12443
|
+
legacy = { branch, sha: git(["rev-parse", "FETCH_HEAD"], dotDir).stdout };
|
|
12444
|
+
} else if (isMissingRemoteRef(old.stderr)) {
|
|
12445
|
+
legacyAbsent = true;
|
|
12358
12446
|
}
|
|
12359
|
-
|
|
12447
|
+
}
|
|
12448
|
+
for (const incoming of [fetched ? tracking : null, legacy?.sha ?? null]) {
|
|
12449
|
+
if (!incoming) continue;
|
|
12450
|
+
const merged = mergeIncoming(dotDir, incoming, result);
|
|
12451
|
+
if (!merged.ok) return finish(dotDir, merged.result);
|
|
12452
|
+
result.pulled += merged.pulled;
|
|
12360
12453
|
}
|
|
12361
12454
|
}
|
|
12362
12455
|
if (push) {
|
|
12363
|
-
const
|
|
12456
|
+
const known = git(["rev-parse", "--verify", "--quiet", tracking], dotDir).ok;
|
|
12457
|
+
const ahead = commitCount(dotDir, known ? `${tracking}..HEAD` : "HEAD");
|
|
12364
12458
|
if (ahead > 0) {
|
|
12365
|
-
const pushed = git(["push", "--quiet", remote, `HEAD
|
|
12459
|
+
const pushed = git(["push", "--quiet", remote, `HEAD:${remoteRef}`], dotDir);
|
|
12366
12460
|
if (!pushed.ok) {
|
|
12367
12461
|
const rejected = /rejected|non-fast-forward|fetch first/i.test(pushed.stderr);
|
|
12368
12462
|
return finish(dotDir, {
|
|
@@ -12372,8 +12466,24 @@ function syncBoard(dotDir, options = {}) {
|
|
|
12372
12466
|
warning: rejected ? `push to ${remote} rejected (the remote moved) \u2014 run sync again` : `push to ${remote} failed: ${firstLine(pushed.stderr)}`
|
|
12373
12467
|
});
|
|
12374
12468
|
}
|
|
12469
|
+
git(["update-ref", tracking, "HEAD"], dotDir);
|
|
12375
12470
|
result.pushed = ahead;
|
|
12376
12471
|
}
|
|
12472
|
+
if (legacy && git(["merge-base", "--is-ancestor", legacy.sha, tracking], dotDir).ok) {
|
|
12473
|
+
const removed = git(
|
|
12474
|
+
["push", "--quiet", `--force-with-lease=refs/heads/${legacy.branch}:${legacy.sha}`, remote, `:refs/heads/${legacy.branch}`],
|
|
12475
|
+
dotDir
|
|
12476
|
+
);
|
|
12477
|
+
if (removed.ok) {
|
|
12478
|
+
git(["update-ref", "-d", `refs/remotes/${remote}/${legacy.branch}`], dotDir);
|
|
12479
|
+
result.movedFromBranch = legacy.branch;
|
|
12480
|
+
configSet(dotDir, LEGACY_CHECKED_CONFIG_KEY, remote);
|
|
12481
|
+
}
|
|
12482
|
+
}
|
|
12483
|
+
}
|
|
12484
|
+
if (legacyAbsent) {
|
|
12485
|
+
git(["update-ref", "-d", `refs/remotes/${remote}/${legacyRemoteBranch(dotDir)}`], dotDir);
|
|
12486
|
+
configSet(dotDir, LEGACY_CHECKED_CONFIG_KEY, remote);
|
|
12377
12487
|
}
|
|
12378
12488
|
return finish(dotDir, result);
|
|
12379
12489
|
}
|
|
@@ -12381,7 +12491,7 @@ function finish(dotDir, result) {
|
|
|
12381
12491
|
writeSyncState(dotDir, {
|
|
12382
12492
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12383
12493
|
remote: result.remote ?? "",
|
|
12384
|
-
|
|
12494
|
+
remoteRef: result.remoteRef ?? "",
|
|
12385
12495
|
ok: result.ok,
|
|
12386
12496
|
...result.warning ? { warning: result.warning } : {}
|
|
12387
12497
|
});
|
|
@@ -12406,10 +12516,15 @@ function syncStatusLabel(dotDir, now = Date.now()) {
|
|
|
12406
12516
|
function pendingChanges(dotDir) {
|
|
12407
12517
|
const remote = boardRemote(dotDir);
|
|
12408
12518
|
if (!remote) return 0;
|
|
12409
|
-
const tracking =
|
|
12519
|
+
const tracking = boardTrackingRef(remote, boardRemoteName(dotDir));
|
|
12410
12520
|
const known = git(["rev-parse", "--verify", "--quiet", tracking], dotDir).ok;
|
|
12411
12521
|
return commitCount(dotDir, known ? `${tracking}..HEAD` : "HEAD");
|
|
12412
12522
|
}
|
|
12523
|
+
function boardWebUrl(remoteUrl, remoteRef) {
|
|
12524
|
+
if (!remoteUrl) return null;
|
|
12525
|
+
const m = /^(?:https:\/\/(?:[^@/]+@)?github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(remoteUrl);
|
|
12526
|
+
return m ? `https://github.com/${m[1]}/${m[2]}/commits/${remoteRef}` : null;
|
|
12527
|
+
}
|
|
12413
12528
|
var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
12414
12529
|
function syncMessage(result) {
|
|
12415
12530
|
if (result.skipped === "not-tracked") {
|
|
@@ -13442,7 +13557,6 @@ export {
|
|
|
13442
13557
|
boardIsDirty,
|
|
13443
13558
|
commitBoard,
|
|
13444
13559
|
commitHandEdits,
|
|
13445
|
-
resolveAgentName,
|
|
13446
13560
|
homeBoardDir,
|
|
13447
13561
|
isGlobalBoardRequested,
|
|
13448
13562
|
describeCommandForCommit,
|
|
@@ -13456,7 +13570,7 @@ export {
|
|
|
13456
13570
|
import_core5 as import_core,
|
|
13457
13571
|
mergeDriverCommand,
|
|
13458
13572
|
boardRemote,
|
|
13459
|
-
|
|
13573
|
+
boardRemoteRef,
|
|
13460
13574
|
boardAutosync,
|
|
13461
13575
|
setBoardAutosync,
|
|
13462
13576
|
setBoardRemote,
|
|
@@ -13467,6 +13581,7 @@ export {
|
|
|
13467
13581
|
secondsSinceSync,
|
|
13468
13582
|
syncStatusLabel,
|
|
13469
13583
|
pendingChanges,
|
|
13584
|
+
boardWebUrl,
|
|
13470
13585
|
syncMessage,
|
|
13471
13586
|
effectiveAutosync,
|
|
13472
13587
|
clearPushLock,
|