brainfile 0.21.0 → 0.21.2
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-7KU2R7QC.mjs} +245 -101
- 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-3Z7ZJV7M.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
|
}
|
|
@@ -5622,7 +5622,7 @@ var require_frontmatter = __commonJS({
|
|
|
5622
5622
|
}
|
|
5623
5623
|
let endIndex = -1;
|
|
5624
5624
|
for (let i = 1; i < lines.length; i++) {
|
|
5625
|
-
if (lines[i].
|
|
5625
|
+
if (lines[i].trimEnd() === "---") {
|
|
5626
5626
|
endIndex = i;
|
|
5627
5627
|
break;
|
|
5628
5628
|
}
|
|
@@ -8577,6 +8577,7 @@ var require_workspace = __commonJS({
|
|
|
8577
8577
|
exports.getLogFilePath = getLogFilePath2;
|
|
8578
8578
|
exports.findV2Task = findV2Task2;
|
|
8579
8579
|
exports.extractDescription = extractDescription2;
|
|
8580
|
+
exports.replaceDescription = replaceDescription;
|
|
8580
8581
|
exports.extractLog = extractLog2;
|
|
8581
8582
|
exports.composeBody = composeBody2;
|
|
8582
8583
|
exports.readV2BoardConfig = readV2BoardConfig2;
|
|
@@ -8646,10 +8647,27 @@ var require_workspace = __commonJS({
|
|
|
8646
8647
|
}
|
|
8647
8648
|
return null;
|
|
8648
8649
|
}
|
|
8650
|
+
var DESCRIPTION_SECTION = /## Description\n([\s\S]*?)(?=\n## (?:Log|Child Tasks)[ \t]*(?:\n|$)|\n*$)/;
|
|
8649
8651
|
function extractDescription2(body) {
|
|
8650
|
-
const match = body.match(
|
|
8652
|
+
const match = body.match(DESCRIPTION_SECTION);
|
|
8651
8653
|
return match ? match[1].trim() || void 0 : void 0;
|
|
8652
8654
|
}
|
|
8655
|
+
function replaceDescription(body, description) {
|
|
8656
|
+
const match = DESCRIPTION_SECTION.exec(body);
|
|
8657
|
+
if (!match)
|
|
8658
|
+
return null;
|
|
8659
|
+
const before = body.slice(0, match.index);
|
|
8660
|
+
const rest = body.slice(match.index + match[0].length).replace(/^\n+/, "");
|
|
8661
|
+
const text = description?.trim();
|
|
8662
|
+
if (!text)
|
|
8663
|
+
return `${before}${rest}`;
|
|
8664
|
+
return rest ? `${before}## Description
|
|
8665
|
+
${text}
|
|
8666
|
+
|
|
8667
|
+
${rest}` : `${before}## Description
|
|
8668
|
+
${text}
|
|
8669
|
+
`;
|
|
8670
|
+
}
|
|
8653
8671
|
function extractLog2(body) {
|
|
8654
8672
|
const match = body.match(/## Log\n([\s\S]*?)(?=\n## |\n*$)/);
|
|
8655
8673
|
return match ? match[1].trim() || void 0 : void 0;
|
|
@@ -9471,6 +9489,7 @@ var require_taskOperations = __commonJS({
|
|
|
9471
9489
|
exports.getEffectiveState = getEffectiveState;
|
|
9472
9490
|
var fs11 = __importStar(__require("fs"));
|
|
9473
9491
|
var path11 = __importStar(__require("path"));
|
|
9492
|
+
var workspace_1 = require_workspace();
|
|
9474
9493
|
var boardValidation_1 = require_boardValidation();
|
|
9475
9494
|
var contractSpec_1 = require_contractSpec();
|
|
9476
9495
|
var idGen_1 = require_idGen();
|
|
@@ -9772,7 +9791,14 @@ ${section}
|
|
|
9772
9791
|
task[key] = value;
|
|
9773
9792
|
}
|
|
9774
9793
|
};
|
|
9775
|
-
|
|
9794
|
+
let body = doc.body;
|
|
9795
|
+
const inBody = patch.description === void 0 ? null : (0, workspace_1.replaceDescription)(body, patch.description);
|
|
9796
|
+
if (inBody !== null) {
|
|
9797
|
+
body = inBody;
|
|
9798
|
+
delete task.description;
|
|
9799
|
+
} else {
|
|
9800
|
+
applyField("description", patch.description);
|
|
9801
|
+
}
|
|
9776
9802
|
applyField("priority", patch.priority);
|
|
9777
9803
|
applyField("tags", patch.tags);
|
|
9778
9804
|
applyField("assignee", patch.assignee);
|
|
@@ -9782,7 +9808,7 @@ ${section}
|
|
|
9782
9808
|
applyField("status", patch.status);
|
|
9783
9809
|
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9784
9810
|
try {
|
|
9785
|
-
(0, taskFile_1.writeTaskFile)(taskPath, task,
|
|
9811
|
+
(0, taskFile_1.writeTaskFile)(taskPath, task, body);
|
|
9786
9812
|
return { success: true, task, filePath: taskPath };
|
|
9787
9813
|
} catch (err) {
|
|
9788
9814
|
return { success: false, error: `Failed to write task file: ${err}` };
|
|
@@ -10677,7 +10703,7 @@ var require_dist = __commonJS({
|
|
|
10677
10703
|
exports.patchTasks = exports.moveTasks = exports.setAllSubtasksCompleted = exports.setSubtasksCompleted = exports.updateSubtask = exports.deleteSubtask = exports.addSubtask = exports.patchTask = exports.restoreTask = exports.archiveTask = exports.updateStatsConfig = exports.updateBoardTitle = exports.toggleSubtask = exports.deleteTask = exports.updateTask = exports.addTask = exports.moveTask = exports.getAllTemplateIds = exports.getTemplateById = exports.processTemplate = exports.generateSubtaskId = exports.generateTaskId = exports.BUILT_IN_TEMPLATES = exports.hashBoardContent = exports.hashBoard = exports.diffBoards = exports.BrainfileLinter = exports.BrainfileValidator = exports.serializeFrontmatter = exports.parseFrontmatter = exports.BrainfileSerializer = exports.loadSchemaHints = exports.parseSchemaHints = exports.inferRenderer = exports.inferType = exports.BrainfileParser = exports.buildContract = exports.parseDeliverableSpec = exports.normalizeToArray = exports.ALLOWED_DELIVERABLE_TYPES = exports.removeTaskContractConstraint = exports.addTaskContractConstraint = exports.removeTaskContractValidationCommand = exports.addTaskContractValidationCommand = exports.removeTaskContractDeliverable = exports.addTaskContractDeliverable = exports.patchTaskContract = exports.setTaskContractStatus = exports.clearTaskContract = exports.setTaskContract = void 0;
|
|
10678
10704
|
exports.BRAINFILE_BASENAME = exports.DOT_BRAINFILE_DIRNAME = exports.atomicWriteFileSync = exports.ensureDotBrainfileGitignore = exports.ensureDotBrainfileDir = exports.getDotBrainfileGitignorePath = exports.getBrainfileStatePath = exports.getBrainfileStateDir = exports.resolveBrainfilePath = exports.findBrainfile = exports.EXCLUDE_DIRS = exports.BRAINFILE_GLOBS = exports.BRAINFILE_PATTERNS = exports.extractBrainfileSuffix = exports.isBrainfileName = exports.watchBrainfiles = exports.findNearestBrainfile = exports.findPrimaryBrainfile = exports.discover = exports.getParentTaskId = exports.isValidSubtaskId = exports.isValidTaskId = exports.generateNextSubtaskId = exports.generateSubtaskIdFromIndex = exports.generateNextTaskId = exports.getMaxTaskIdNumber = exports.extractTaskIdNumber = exports.getOverdueTasks = exports.getIncompleteSubtasksSummary = exports.getTasksWithIncompleteSubtasks = exports.isCompletionColumn = exports.findCompletionColumn = exports.columnExists = exports.getTotalTaskCount = exports.getColumnTaskCount = exports.searchTasks = exports.getTasksByAssignee = exports.getTasksByPriority = exports.getTasksByTag = exports.getAllTasks = exports.taskIdExists = exports.findTaskById = exports.findColumnByName = exports.findColumnById = exports.validateColumn = exports.validateType = exports.isTypeCompletable = exports.getBoardTypes = exports.archiveTasks = exports.deleteTasks = void 0;
|
|
10679
10705
|
exports.activateTaskContract = exports.attachTaskContract = exports.failTaskContract = exports.completeTaskContract = exports.deliverTaskContract = exports.pickupTaskContract = exports.updateSubtasksInFile = exports.toggleSubtasksInFile = exports.deleteSubtasksFromFile = exports.addSubtasksToFile = exports.searchLogs = exports.searchTaskFiles = exports.findTask = exports.listTasks = exports.appendLog = exports.deleteTaskFile = exports.completeTaskFile = exports.patchTaskFile = exports.moveTaskFileToColumn = exports.moveTaskFile = exports.addTaskFile = exports.generateNextFileTaskId = exports.DEFAULT_CONTRACT_COLUMN_MAP = exports.taskFileName = exports.readTasksDir = exports.writeTaskFile = exports.readTaskFile = exports.serializeTaskContent = exports.parseTaskContent = exports.writeBoardConfig = exports.readBoardConfig = exports.serializeBoardConfig = exports.parseBoardConfig = exports.formatTaskForLinear = exports.formatTaskForGitHub = exports.BRIEF_STATE_VERSION = exports.sanitizeAgentFilename = exports.getBriefStatePath = exports.getBriefStateDir = exports.writeBriefState = exports.readBriefState = exports.relativeTime = exports.parseNotes = exports.parseNoteLine = exports.isEmptyBrief = exports.buildBrief = exports.DOT_BRAINFILE_STATE_IGNORE_ENTRY = exports.DOT_BRAINFILE_STATE_DIRNAME = exports.DOT_BRAINFILE_GITIGNORE_BASENAME = exports.BRAINFILE_STATE_BASENAME = void 0;
|
|
10680
|
-
exports.Brainfile = exports.buildBoardFromV2 = exports.readV2BoardConfig = exports.composeBody = exports.extractLog = exports.extractDescription = exports.findV2Task = exports.getLogFilePath = exports.getTaskFilePath = exports.ensureV2Dirs = exports.isV2 = exports.getV2Dirs = exports.topologicalSort = exports.DependencyCycleError = exports.MissingDependencyError = exports.ledgerRecordToCompletedRef = exports.taskToCompletedRef = exports.isLedgerContractStatus = exports.normalizePathValue = exports.getTaskContext = exports.getFileHistory = exports.queryLedger = exports.readLedger = exports.appendLedgerRecord = exports.buildLedgerRecord = exports.searchTasksRanked = exports.scoreTaskDocument = exports.taskMatchesFilters = exports.parseSearchQuery = exports.getEffectiveState = exports.activateTaskContractsByParent = void 0;
|
|
10706
|
+
exports.Brainfile = exports.buildBoardFromV2 = exports.readV2BoardConfig = exports.composeBody = exports.extractLog = exports.replaceDescription = exports.extractDescription = exports.findV2Task = exports.getLogFilePath = exports.getTaskFilePath = exports.ensureV2Dirs = exports.isV2 = exports.getV2Dirs = exports.topologicalSort = exports.DependencyCycleError = exports.MissingDependencyError = exports.ledgerRecordToCompletedRef = exports.taskToCompletedRef = exports.isLedgerContractStatus = exports.normalizePathValue = exports.getTaskContext = exports.getFileHistory = exports.queryLedger = exports.readLedger = exports.appendLedgerRecord = exports.buildLedgerRecord = exports.searchTasksRanked = exports.scoreTaskDocument = exports.taskMatchesFilters = exports.parseSearchQuery = exports.getEffectiveState = exports.activateTaskContractsByParent = void 0;
|
|
10681
10707
|
__exportStar(require_types2(), exports);
|
|
10682
10708
|
__exportStar(require_contract(), exports);
|
|
10683
10709
|
var contract_1 = require_contract2();
|
|
@@ -11236,6 +11262,9 @@ var require_dist = __commonJS({
|
|
|
11236
11262
|
Object.defineProperty(exports, "extractDescription", { enumerable: true, get: function() {
|
|
11237
11263
|
return workspace_1.extractDescription;
|
|
11238
11264
|
} });
|
|
11265
|
+
Object.defineProperty(exports, "replaceDescription", { enumerable: true, get: function() {
|
|
11266
|
+
return workspace_1.replaceDescription;
|
|
11267
|
+
} });
|
|
11239
11268
|
Object.defineProperty(exports, "extractLog", { enumerable: true, get: function() {
|
|
11240
11269
|
return workspace_1.extractLog;
|
|
11241
11270
|
} });
|
|
@@ -11549,18 +11578,34 @@ import * as path2 from "path";
|
|
|
11549
11578
|
import { spawnSync } from "child_process";
|
|
11550
11579
|
var DEFAULT_BOARD_BRANCH = "brainfile";
|
|
11551
11580
|
var BOARD_BRANCH_CONFIG_KEY = "brainfile.branch";
|
|
11581
|
+
var BOARD_REF_NAMESPACE = "refs/brainfile";
|
|
11582
|
+
var BOARD_NAME_CONFIG_KEY = "brainfile.boardName";
|
|
11583
|
+
var LEGACY_REMOTE_BRANCH_CONFIG_KEY = "brainfile.remoteBranch";
|
|
11584
|
+
var DEFAULT_BOARD_NAME = "board";
|
|
11585
|
+
function remoteBoardRef(name) {
|
|
11586
|
+
return `${BOARD_REF_NAMESPACE}/${name}`;
|
|
11587
|
+
}
|
|
11588
|
+
function boardTrackingRef(remote, name) {
|
|
11589
|
+
return `${BOARD_REF_NAMESPACE}/remotes/${remote}/${name}`;
|
|
11590
|
+
}
|
|
11591
|
+
var NETWORK_TIMEOUT_MS = 8e3;
|
|
11592
|
+
var NO_PROMPT_ENV = { GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "echo", SSH_ASKPASS: "echo" };
|
|
11593
|
+
function gitNetwork(args, cwd) {
|
|
11594
|
+
return git(args, cwd, NO_PROMPT_ENV, NETWORK_TIMEOUT_MS);
|
|
11595
|
+
}
|
|
11552
11596
|
var EXCLUDE_ENTRY = ".brainfile/";
|
|
11553
11597
|
var EXCLUDE_MARKER = "# brainfile: board worktree (managed by `brainfile`)";
|
|
11554
11598
|
var FALLBACK_IDENTITY_NAME = "brainfile";
|
|
11555
11599
|
var FALLBACK_IDENTITY_EMAIL = "brainfile@localhost";
|
|
11556
11600
|
var MAX_MESSAGE_LENGTH = 72;
|
|
11557
11601
|
var BOARD_SUBDIRS = ["board", "logs"];
|
|
11558
|
-
function git(args, cwd, env) {
|
|
11602
|
+
function git(args, cwd, env, timeoutMs) {
|
|
11559
11603
|
const result = spawnSync("git", args, {
|
|
11560
11604
|
cwd,
|
|
11561
11605
|
encoding: "utf-8",
|
|
11562
11606
|
env: env ? { ...process.env, ...env } : process.env,
|
|
11563
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
11607
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
11608
|
+
...timeoutMs ? { timeout: timeoutMs, killSignal: "SIGKILL" } : {}
|
|
11564
11609
|
});
|
|
11565
11610
|
return {
|
|
11566
11611
|
ok: result.status === 0,
|
|
@@ -11625,25 +11670,75 @@ function findTrackedBoardDir(cwd) {
|
|
|
11625
11670
|
if (!hit) return null;
|
|
11626
11671
|
return fs2.existsSync(path2.join(hit.path, "brainfile.md")) ? hit.path : null;
|
|
11627
11672
|
}
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
const
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11673
|
+
var PROBE_MISS_TTL_MS = 10 * 6e4;
|
|
11674
|
+
function probeMissFile(cwd) {
|
|
11675
|
+
const common = gitCommonDir(cwd);
|
|
11676
|
+
return common ? path2.join(common, "brainfile-probe.json") : null;
|
|
11677
|
+
}
|
|
11678
|
+
function recentProbeMiss(cwd, remote) {
|
|
11679
|
+
const file = probeMissFile(cwd);
|
|
11680
|
+
if (!file) return false;
|
|
11681
|
+
try {
|
|
11682
|
+
const miss = JSON.parse(fs2.readFileSync(file, "utf-8"));
|
|
11683
|
+
return miss.remote === remote && typeof miss.at === "number" && Date.now() - miss.at < PROBE_MISS_TTL_MS;
|
|
11684
|
+
} catch {
|
|
11685
|
+
return false;
|
|
11686
|
+
}
|
|
11687
|
+
}
|
|
11688
|
+
function recordProbeMiss(cwd, remote) {
|
|
11689
|
+
const file = probeMissFile(cwd);
|
|
11690
|
+
if (!file) return;
|
|
11691
|
+
try {
|
|
11692
|
+
fs2.writeFileSync(file, JSON.stringify({ remote, at: Date.now() }), "utf-8");
|
|
11693
|
+
} catch {
|
|
11645
11694
|
}
|
|
11646
|
-
|
|
11695
|
+
}
|
|
11696
|
+
function clearProbeMiss(cwd) {
|
|
11697
|
+
const file = probeMissFile(cwd);
|
|
11698
|
+
if (file) fs2.rmSync(file, { force: true });
|
|
11699
|
+
}
|
|
11700
|
+
function listRemoteBoards(cwd, remote) {
|
|
11701
|
+
const r = gitNetwork(["ls-remote", "--quiet", remote, `${BOARD_REF_NAMESPACE}/*`], cwd);
|
|
11702
|
+
if (!r.ok) return null;
|
|
11703
|
+
const names = [];
|
|
11704
|
+
for (const line of r.stdout.split("\n")) {
|
|
11705
|
+
const ref = line.split(" ")[1] ?? "";
|
|
11706
|
+
const name = ref.slice(BOARD_REF_NAMESPACE.length + 1);
|
|
11707
|
+
if (ref.startsWith(`${BOARD_REF_NAMESPACE}/`) && name && !name.includes("/")) names.push(name);
|
|
11708
|
+
}
|
|
11709
|
+
return names;
|
|
11710
|
+
}
|
|
11711
|
+
function findBoardStart(cwd, branch, options) {
|
|
11712
|
+
if (git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], cwd).ok) return { ref: branch, remote: null };
|
|
11713
|
+
const configuredRemote = git(["config", "--get", "brainfile.remote"], cwd).stdout || null;
|
|
11714
|
+
const remote = configuredRemote ?? (git(["remote", "get-url", "origin"], cwd).ok ? "origin" : null);
|
|
11715
|
+
if (!remote) return null;
|
|
11716
|
+
const configuredName = git(["config", "--get", BOARD_NAME_CONFIG_KEY], cwd).stdout || git(["config", "--get", LEGACY_REMOTE_BRANCH_CONFIG_KEY], cwd).stdout || null;
|
|
11717
|
+
const knownName = configuredName ?? DEFAULT_BOARD_NAME;
|
|
11718
|
+
const known = boardTrackingRef(remote, knownName);
|
|
11719
|
+
if (git(["rev-parse", "--verify", "--quiet", known], cwd).ok) return { ref: known, remote, name: knownName };
|
|
11720
|
+
if (options.fresh || !recentProbeMiss(cwd, remote)) {
|
|
11721
|
+
const names = listRemoteBoards(cwd, remote);
|
|
11722
|
+
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;
|
|
11723
|
+
if (name) {
|
|
11724
|
+
const tracking = boardTrackingRef(remote, name);
|
|
11725
|
+
if (gitNetwork(["fetch", "--quiet", "--no-tags", remote, `+${remoteBoardRef(name)}:${tracking}`], cwd).ok) {
|
|
11726
|
+
clearProbeMiss(cwd);
|
|
11727
|
+
return { ref: tracking, remote, name };
|
|
11728
|
+
}
|
|
11729
|
+
}
|
|
11730
|
+
}
|
|
11731
|
+
const legacyBranch = configuredName ?? branch;
|
|
11732
|
+
const legacy = `refs/remotes/${remote}/${legacyBranch}`;
|
|
11733
|
+
if (!git(["rev-parse", "--verify", "--quiet", legacy], cwd).ok && configuredRemote && (options.fresh || !recentProbeMiss(cwd, remote))) {
|
|
11734
|
+
gitNetwork(["fetch", "--quiet", "--no-tags", remote, `+refs/heads/${legacyBranch}:${legacy}`], cwd);
|
|
11735
|
+
}
|
|
11736
|
+
if (git(["rev-parse", "--verify", "--quiet", legacy], cwd).ok) return { ref: legacy, remote };
|
|
11737
|
+
recordProbeMiss(cwd, remote);
|
|
11738
|
+
return null;
|
|
11739
|
+
}
|
|
11740
|
+
function materializeBoardWorktree(cwd, options = {}) {
|
|
11741
|
+
const branch = boardBranch(cwd);
|
|
11647
11742
|
const worktrees = listWorktrees(cwd);
|
|
11648
11743
|
if (worktrees.length === 0) return null;
|
|
11649
11744
|
if (worktrees.some((w) => w.branch === `refs/heads/${branch}`)) return null;
|
|
@@ -11657,15 +11752,21 @@ function materializeBoardWorktree(cwd) {
|
|
|
11657
11752
|
target = path2.join(main.path, ".brainfile");
|
|
11658
11753
|
}
|
|
11659
11754
|
if (fs2.existsSync(target)) return null;
|
|
11660
|
-
const
|
|
11755
|
+
const start = findBoardStart(cwd, branch, options);
|
|
11756
|
+
if (!start) return null;
|
|
11757
|
+
const r = start.remote ? git(["worktree", "add", "--quiet", "--no-track", "-b", branch, target, start.ref], cwd) : git(["worktree", "add", "--quiet", target, branch], cwd);
|
|
11661
11758
|
if (!r.ok) return null;
|
|
11662
11759
|
ensureExcludeEntry(cwd);
|
|
11663
11760
|
for (const sub of BOARD_SUBDIRS) fs2.mkdirSync(path2.join(target, sub), { recursive: true });
|
|
11664
|
-
|
|
11665
|
-
|
|
11761
|
+
if (start.remote) {
|
|
11762
|
+
if (!git(["config", "--get", "brainfile.remote"], cwd).stdout) git(["config", "brainfile.remote", start.remote], cwd);
|
|
11763
|
+
if (start.name && start.name !== DEFAULT_BOARD_NAME && !git(["config", "--get", BOARD_NAME_CONFIG_KEY], cwd).stdout) {
|
|
11764
|
+
git(["config", BOARD_NAME_CONFIG_KEY, start.name], cwd);
|
|
11765
|
+
}
|
|
11766
|
+
}
|
|
11666
11767
|
const relative3 = path2.relative(process.cwd(), target) || ".";
|
|
11667
11768
|
process.stderr.write(
|
|
11668
|
-
|
|
11769
|
+
start.remote ? `Checked out the shared board from ${start.remote} into ${relative3}/
|
|
11669
11770
|
` : `Checked out the board from the '${branch}' branch into ${relative3}/
|
|
11670
11771
|
`
|
|
11671
11772
|
);
|
|
@@ -11731,9 +11832,6 @@ function identityArgs(dotDir) {
|
|
|
11731
11832
|
if (!git(["config", "--get", "user.email"], dotDir).stdout) args.push("-c", `user.email=${FALLBACK_IDENTITY_EMAIL}`);
|
|
11732
11833
|
return args;
|
|
11733
11834
|
}
|
|
11734
|
-
function sanitizeAgent(agent) {
|
|
11735
|
-
return agent.replace(/[<>\r\n]/g, "").trim();
|
|
11736
|
-
}
|
|
11737
11835
|
var commitListeners = [];
|
|
11738
11836
|
function onBoardCommitted(listener) {
|
|
11739
11837
|
if (!commitListeners.includes(listener)) commitListeners.push(listener);
|
|
@@ -11768,17 +11866,12 @@ function commitBoard(dotDir, options) {
|
|
|
11768
11866
|
if (unmergedFiles(dotDir).length > 0) return false;
|
|
11769
11867
|
if (!git(["add", "-A"], dotDir).ok) return false;
|
|
11770
11868
|
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);
|
|
11869
|
+
const agent = options.agent?.replace(/[[\]<>\r\n]/g, "").trim();
|
|
11870
|
+
const suffix = agent ? ` [${agent}]` : "";
|
|
11871
|
+
const message = oneLine(options.message, suffix);
|
|
11778
11872
|
const r = git(
|
|
11779
11873
|
[...identityArgs(dotDir), "-c", "commit.gpgsign=false", "commit", "--quiet", "--no-verify", "-m", message],
|
|
11780
|
-
dotDir
|
|
11781
|
-
env
|
|
11874
|
+
dotDir
|
|
11782
11875
|
);
|
|
11783
11876
|
if (r.ok) notifyCommitted(dotDir);
|
|
11784
11877
|
return r.ok;
|
|
@@ -11794,13 +11887,6 @@ function commitHandEdits(dotDir) {
|
|
|
11794
11887
|
const summary = files.length <= 3 ? files.join(", ") : `${files.length} files`;
|
|
11795
11888
|
return commitBoard(dotDir, { message: `edit: ${summary}` });
|
|
11796
11889
|
}
|
|
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
11890
|
function homeBoardDir() {
|
|
11805
11891
|
return path2.join(os.homedir(), ".brainfile");
|
|
11806
11892
|
}
|
|
@@ -11808,9 +11894,10 @@ function isGlobalBoardRequested(argv = process.argv) {
|
|
|
11808
11894
|
if (process.env.BRAINFILE_GLOBAL === "1") return true;
|
|
11809
11895
|
return argv.includes("-g") || argv.includes("--global");
|
|
11810
11896
|
}
|
|
11811
|
-
function oneLine(message) {
|
|
11897
|
+
function oneLine(message, suffix = "") {
|
|
11812
11898
|
const flat = message.replace(/\s+/g, " ").trim();
|
|
11813
|
-
|
|
11899
|
+
const room = Math.max(1, MAX_MESSAGE_LENGTH - suffix.length);
|
|
11900
|
+
return `${flat.length > room ? `${flat.slice(0, room - 1)}\u2026` : flat}${suffix}`;
|
|
11814
11901
|
}
|
|
11815
11902
|
function describeCommandForCommit(command) {
|
|
11816
11903
|
const names = [];
|
|
@@ -11833,7 +11920,7 @@ var exitHookInstalled = false;
|
|
|
11833
11920
|
function scheduleBoardCommit(brainfilePath, message, delayMs = 2e3) {
|
|
11834
11921
|
const dotDir = path2.dirname(path2.resolve(brainfilePath));
|
|
11835
11922
|
if (!isTrackedBoard(dotDir)) return;
|
|
11836
|
-
const entry = pendingCommits.get(dotDir) ?? { messages: []
|
|
11923
|
+
const entry = pendingCommits.get(dotDir) ?? { messages: [] };
|
|
11837
11924
|
entry.messages.push(message);
|
|
11838
11925
|
pendingCommits.set(dotDir, entry);
|
|
11839
11926
|
if (flushTimer) clearTimeout(flushTimer);
|
|
@@ -11852,7 +11939,7 @@ function flushBoardCommits() {
|
|
|
11852
11939
|
for (const [dotDir, entry] of pendingCommits) {
|
|
11853
11940
|
const [first, ...rest] = entry.messages;
|
|
11854
11941
|
const message = rest.length === 0 ? first : `${first} (+${rest.length} more)`;
|
|
11855
|
-
commitBoard(dotDir, { message
|
|
11942
|
+
commitBoard(dotDir, { message });
|
|
11856
11943
|
}
|
|
11857
11944
|
pendingCommits.clear();
|
|
11858
11945
|
}
|
|
@@ -12130,7 +12217,7 @@ import * as fs7 from "fs";
|
|
|
12130
12217
|
import * as os3 from "os";
|
|
12131
12218
|
import * as path7 from "path";
|
|
12132
12219
|
var REMOTE_CONFIG_KEY = "brainfile.remote";
|
|
12133
|
-
var
|
|
12220
|
+
var LEGACY_CHECKED_CONFIG_KEY = "brainfile.legacyBranchChecked";
|
|
12134
12221
|
var AUTOSYNC_CONFIG_KEY = "brainfile.autosync";
|
|
12135
12222
|
var URL_REMOTE_NAME = "board";
|
|
12136
12223
|
var MERGE_DRIVER_NAME = "brainfile";
|
|
@@ -12152,14 +12239,23 @@ function configSet(dotDir, key, value) {
|
|
|
12152
12239
|
function boardRemote(dotDir) {
|
|
12153
12240
|
return configGet(dotDir, REMOTE_CONFIG_KEY);
|
|
12154
12241
|
}
|
|
12155
|
-
function
|
|
12156
|
-
const configured = configGet(dotDir,
|
|
12242
|
+
function boardRemoteName(dotDir) {
|
|
12243
|
+
const configured = configGet(dotDir, BOARD_NAME_CONFIG_KEY) ?? configGet(dotDir, LEGACY_REMOTE_BRANCH_CONFIG_KEY);
|
|
12157
12244
|
if (configured) return configured;
|
|
12245
|
+
return defaultName(dotDir, DEFAULT_BOARD_NAME);
|
|
12246
|
+
}
|
|
12247
|
+
function defaultName(dotDir, inRepo) {
|
|
12158
12248
|
if (path7.resolve(dotDir) === path7.resolve(homeBoardDir())) return "home";
|
|
12159
12249
|
if (boardRepoKind(dotDir) === "standalone" && path7.basename(path7.resolve(dotDir)) === ".brainfile") {
|
|
12160
|
-
return path7.basename(path7.dirname(path7.resolve(dotDir))) ||
|
|
12250
|
+
return path7.basename(path7.dirname(path7.resolve(dotDir))) || inRepo;
|
|
12161
12251
|
}
|
|
12162
|
-
return
|
|
12252
|
+
return inRepo;
|
|
12253
|
+
}
|
|
12254
|
+
function boardRemoteRef(dotDir) {
|
|
12255
|
+
return remoteBoardRef(boardRemoteName(dotDir));
|
|
12256
|
+
}
|
|
12257
|
+
function legacyRemoteBranch(dotDir) {
|
|
12258
|
+
return configGet(dotDir, LEGACY_REMOTE_BRANCH_CONFIG_KEY) ?? defaultName(dotDir, boardBranch(dotDir));
|
|
12163
12259
|
}
|
|
12164
12260
|
function boardAutosync(dotDir) {
|
|
12165
12261
|
const value = configGet(dotDir, AUTOSYNC_CONFIG_KEY);
|
|
@@ -12176,7 +12272,7 @@ function looksLikeRemoteUrl(target) {
|
|
|
12176
12272
|
if (target.includes("/") && fs7.existsSync(target)) return true;
|
|
12177
12273
|
return false;
|
|
12178
12274
|
}
|
|
12179
|
-
function setBoardRemote(cwd, target,
|
|
12275
|
+
function setBoardRemote(cwd, target, boardName) {
|
|
12180
12276
|
const trimmed = target.trim();
|
|
12181
12277
|
if (!trimmed) throw new Error("remote is required");
|
|
12182
12278
|
let remote;
|
|
@@ -12194,13 +12290,16 @@ function setBoardRemote(cwd, target, remoteBranch) {
|
|
|
12194
12290
|
throw new Error(`'${remote}' is not a git remote here.${hint} Pass a URL to add one.`);
|
|
12195
12291
|
}
|
|
12196
12292
|
}
|
|
12293
|
+
if (boardName !== void 0 && !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(boardName)) {
|
|
12294
|
+
throw new Error(`'${boardName}' is not a usable board name: use letters, digits, '.', '_' or '-'.`);
|
|
12295
|
+
}
|
|
12197
12296
|
configSet(cwd, REMOTE_CONFIG_KEY, remote);
|
|
12198
|
-
if (
|
|
12297
|
+
if (boardName) configSet(cwd, BOARD_NAME_CONFIG_KEY, boardName);
|
|
12199
12298
|
registerMergeDriver(cwd);
|
|
12200
12299
|
return {
|
|
12201
12300
|
remote,
|
|
12202
12301
|
url: git(["remote", "get-url", remote], cwd).stdout || null,
|
|
12203
|
-
|
|
12302
|
+
remoteRef: boardRemoteRef(cwd)
|
|
12204
12303
|
};
|
|
12205
12304
|
}
|
|
12206
12305
|
function registerMergeDriver(cwd, command) {
|
|
@@ -12255,18 +12354,12 @@ function commitCount(dotDir, range) {
|
|
|
12255
12354
|
const r = git(["rev-list", "--count", range], dotDir);
|
|
12256
12355
|
return r.ok ? Number.parseInt(r.stdout, 10) || 0 : 0;
|
|
12257
12356
|
}
|
|
12258
|
-
function
|
|
12357
|
+
function identityArgs2(dotDir) {
|
|
12259
12358
|
const args = ["-c", "commit.gpgsign=false"];
|
|
12260
12359
|
if (!git(["config", "--get", "user.email"], dotDir).ok) {
|
|
12261
12360
|
args.push("-c", "user.name=brainfile", "-c", "user.email=brainfile@localhost");
|
|
12262
12361
|
}
|
|
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 };
|
|
12362
|
+
return args;
|
|
12270
12363
|
}
|
|
12271
12364
|
function resolveCompleteVersusEdit(dotDir) {
|
|
12272
12365
|
const status = git(["status", "--porcelain", "-z"], dotDir);
|
|
@@ -12310,16 +12403,49 @@ ${entryLine}
|
|
|
12310
12403
|
}
|
|
12311
12404
|
return resolved;
|
|
12312
12405
|
}
|
|
12406
|
+
function mergeIncoming(dotDir, incoming, result) {
|
|
12407
|
+
if (git(["merge-base", "--is-ancestor", incoming, "HEAD"], dotDir).ok) return { ok: true, pulled: 0 };
|
|
12408
|
+
const before = git(["rev-parse", "HEAD"], dotDir).stdout;
|
|
12409
|
+
const id = identityArgs2(dotDir);
|
|
12410
|
+
const merge = git([...id, "merge", "--quiet", "--no-edit", incoming], dotDir);
|
|
12411
|
+
if (!merge.ok) {
|
|
12412
|
+
resolveCompleteVersusEdit(dotDir);
|
|
12413
|
+
const remaining = unmergedFiles(dotDir);
|
|
12414
|
+
if (remaining.length > 0) {
|
|
12415
|
+
return {
|
|
12416
|
+
ok: false,
|
|
12417
|
+
result: {
|
|
12418
|
+
...result,
|
|
12419
|
+
ok: false,
|
|
12420
|
+
failure: "conflict",
|
|
12421
|
+
conflicts: remaining,
|
|
12422
|
+
warning: `merge left conflicts in ${remaining.join(", ")} \u2014 resolve them in ${dotDir}, then run sync again`
|
|
12423
|
+
}
|
|
12424
|
+
};
|
|
12425
|
+
}
|
|
12426
|
+
const commit = git([...id, "commit", "--quiet", "--no-edit", "--no-verify"], dotDir);
|
|
12427
|
+
if (!commit.ok) {
|
|
12428
|
+
return { ok: false, result: { ...result, ok: false, failure: "error", warning: `merge commit failed: ${firstLine(commit.stderr)}` } };
|
|
12429
|
+
}
|
|
12430
|
+
}
|
|
12431
|
+
return { ok: true, pulled: commitCount(dotDir, `${before}..${incoming}`) };
|
|
12432
|
+
}
|
|
12433
|
+
function legacyChecked(dotDir, remote) {
|
|
12434
|
+
return configGet(dotDir, LEGACY_CHECKED_CONFIG_KEY) === remote;
|
|
12435
|
+
}
|
|
12313
12436
|
function syncBoard(dotDir, options = {}) {
|
|
12314
|
-
const pull = options.pull ?? true;
|
|
12315
12437
|
const push = options.push ?? true;
|
|
12316
12438
|
const result = { ok: true, pulled: 0, pushed: 0, conflicts: [] };
|
|
12317
12439
|
if (!isTrackedBoard(dotDir)) return { ...result, skipped: "not-tracked" };
|
|
12318
12440
|
const remote = boardRemote(dotDir);
|
|
12319
12441
|
if (!remote) return { ...result, skipped: "no-remote" };
|
|
12320
|
-
const
|
|
12442
|
+
const name = boardRemoteName(dotDir);
|
|
12443
|
+
const remoteRef = remoteBoardRef(name);
|
|
12444
|
+
const tracking = boardTrackingRef(remote, name);
|
|
12321
12445
|
result.remote = remote;
|
|
12322
|
-
result.
|
|
12446
|
+
result.remoteRef = remoteRef;
|
|
12447
|
+
const checkLegacy = !legacyChecked(dotDir, remote);
|
|
12448
|
+
const pull = (options.pull ?? true) || checkLegacy;
|
|
12323
12449
|
const stuck = unmergedFiles(dotDir);
|
|
12324
12450
|
if (stuck.length > 0) {
|
|
12325
12451
|
return finish(dotDir, { ...result, ok: false, failure: "conflict", conflicts: stuck, warning: `unresolved conflicts: ${stuck.join(", ")}` });
|
|
@@ -12328,41 +12454,38 @@ function syncBoard(dotDir, options = {}) {
|
|
|
12328
12454
|
if (ensureBoardAttributes(dotDir)) commitBoard(dotDir, { message: "chore: merge attributes" });
|
|
12329
12455
|
registerMergeDriver(dotDir);
|
|
12330
12456
|
let fetched = false;
|
|
12457
|
+
let legacy = null;
|
|
12458
|
+
let legacyAbsent = false;
|
|
12331
12459
|
if (pull) {
|
|
12332
|
-
const fetch2 = git(["fetch", "--quiet", remote,
|
|
12460
|
+
const fetch2 = git(["fetch", "--quiet", "--no-tags", remote, `+${remoteRef}:${tracking}`], dotDir);
|
|
12333
12461
|
if (fetch2.ok) {
|
|
12334
12462
|
fetched = true;
|
|
12335
12463
|
} else if (!isMissingRemoteRef(fetch2.stderr)) {
|
|
12336
12464
|
return finish(dotDir, { ...result, ok: false, failure: "offline", warning: `fetch from ${remote} failed: ${firstLine(fetch2.stderr)}` });
|
|
12465
|
+
} else {
|
|
12466
|
+
git(["update-ref", "-d", tracking], dotDir);
|
|
12337
12467
|
}
|
|
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
|
-
}
|
|
12468
|
+
if (checkLegacy) {
|
|
12469
|
+
const branch = legacyRemoteBranch(dotDir);
|
|
12470
|
+
const old = git(["fetch", "--quiet", "--no-tags", remote, `refs/heads/${branch}`], dotDir);
|
|
12471
|
+
if (old.ok) {
|
|
12472
|
+
legacy = { branch, sha: git(["rev-parse", "FETCH_HEAD"], dotDir).stdout };
|
|
12473
|
+
} else if (isMissingRemoteRef(old.stderr)) {
|
|
12474
|
+
legacyAbsent = true;
|
|
12358
12475
|
}
|
|
12359
|
-
|
|
12476
|
+
}
|
|
12477
|
+
for (const incoming of [fetched ? tracking : null, legacy?.sha ?? null]) {
|
|
12478
|
+
if (!incoming) continue;
|
|
12479
|
+
const merged = mergeIncoming(dotDir, incoming, result);
|
|
12480
|
+
if (!merged.ok) return finish(dotDir, merged.result);
|
|
12481
|
+
result.pulled += merged.pulled;
|
|
12360
12482
|
}
|
|
12361
12483
|
}
|
|
12362
12484
|
if (push) {
|
|
12363
|
-
const
|
|
12485
|
+
const known = git(["rev-parse", "--verify", "--quiet", tracking], dotDir).ok;
|
|
12486
|
+
const ahead = commitCount(dotDir, known ? `${tracking}..HEAD` : "HEAD");
|
|
12364
12487
|
if (ahead > 0) {
|
|
12365
|
-
const pushed = git(["push", "--quiet", remote, `HEAD
|
|
12488
|
+
const pushed = git(["push", "--quiet", remote, `HEAD:${remoteRef}`], dotDir);
|
|
12366
12489
|
if (!pushed.ok) {
|
|
12367
12490
|
const rejected = /rejected|non-fast-forward|fetch first/i.test(pushed.stderr);
|
|
12368
12491
|
return finish(dotDir, {
|
|
@@ -12372,8 +12495,24 @@ function syncBoard(dotDir, options = {}) {
|
|
|
12372
12495
|
warning: rejected ? `push to ${remote} rejected (the remote moved) \u2014 run sync again` : `push to ${remote} failed: ${firstLine(pushed.stderr)}`
|
|
12373
12496
|
});
|
|
12374
12497
|
}
|
|
12498
|
+
git(["update-ref", tracking, "HEAD"], dotDir);
|
|
12375
12499
|
result.pushed = ahead;
|
|
12376
12500
|
}
|
|
12501
|
+
if (legacy && git(["merge-base", "--is-ancestor", legacy.sha, tracking], dotDir).ok) {
|
|
12502
|
+
const removed = git(
|
|
12503
|
+
["push", "--quiet", `--force-with-lease=refs/heads/${legacy.branch}:${legacy.sha}`, remote, `:refs/heads/${legacy.branch}`],
|
|
12504
|
+
dotDir
|
|
12505
|
+
);
|
|
12506
|
+
if (removed.ok) {
|
|
12507
|
+
git(["update-ref", "-d", `refs/remotes/${remote}/${legacy.branch}`], dotDir);
|
|
12508
|
+
result.movedFromBranch = legacy.branch;
|
|
12509
|
+
configSet(dotDir, LEGACY_CHECKED_CONFIG_KEY, remote);
|
|
12510
|
+
}
|
|
12511
|
+
}
|
|
12512
|
+
}
|
|
12513
|
+
if (legacyAbsent) {
|
|
12514
|
+
git(["update-ref", "-d", `refs/remotes/${remote}/${legacyRemoteBranch(dotDir)}`], dotDir);
|
|
12515
|
+
configSet(dotDir, LEGACY_CHECKED_CONFIG_KEY, remote);
|
|
12377
12516
|
}
|
|
12378
12517
|
return finish(dotDir, result);
|
|
12379
12518
|
}
|
|
@@ -12381,7 +12520,7 @@ function finish(dotDir, result) {
|
|
|
12381
12520
|
writeSyncState(dotDir, {
|
|
12382
12521
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12383
12522
|
remote: result.remote ?? "",
|
|
12384
|
-
|
|
12523
|
+
remoteRef: result.remoteRef ?? "",
|
|
12385
12524
|
ok: result.ok,
|
|
12386
12525
|
...result.warning ? { warning: result.warning } : {}
|
|
12387
12526
|
});
|
|
@@ -12406,10 +12545,15 @@ function syncStatusLabel(dotDir, now = Date.now()) {
|
|
|
12406
12545
|
function pendingChanges(dotDir) {
|
|
12407
12546
|
const remote = boardRemote(dotDir);
|
|
12408
12547
|
if (!remote) return 0;
|
|
12409
|
-
const tracking =
|
|
12548
|
+
const tracking = boardTrackingRef(remote, boardRemoteName(dotDir));
|
|
12410
12549
|
const known = git(["rev-parse", "--verify", "--quiet", tracking], dotDir).ok;
|
|
12411
12550
|
return commitCount(dotDir, known ? `${tracking}..HEAD` : "HEAD");
|
|
12412
12551
|
}
|
|
12552
|
+
function boardWebUrl(remoteUrl, remoteRef) {
|
|
12553
|
+
if (!remoteUrl) return null;
|
|
12554
|
+
const m = /^(?:https:\/\/(?:[^@/]+@)?github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(remoteUrl);
|
|
12555
|
+
return m ? `https://github.com/${m[1]}/${m[2]}/commits/${remoteRef}` : null;
|
|
12556
|
+
}
|
|
12413
12557
|
var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
12414
12558
|
function syncMessage(result) {
|
|
12415
12559
|
if (result.skipped === "not-tracked") {
|
|
@@ -13442,7 +13586,6 @@ export {
|
|
|
13442
13586
|
boardIsDirty,
|
|
13443
13587
|
commitBoard,
|
|
13444
13588
|
commitHandEdits,
|
|
13445
|
-
resolveAgentName,
|
|
13446
13589
|
homeBoardDir,
|
|
13447
13590
|
isGlobalBoardRequested,
|
|
13448
13591
|
describeCommandForCommit,
|
|
@@ -13456,7 +13599,7 @@ export {
|
|
|
13456
13599
|
import_core5 as import_core,
|
|
13457
13600
|
mergeDriverCommand,
|
|
13458
13601
|
boardRemote,
|
|
13459
|
-
|
|
13602
|
+
boardRemoteRef,
|
|
13460
13603
|
boardAutosync,
|
|
13461
13604
|
setBoardAutosync,
|
|
13462
13605
|
setBoardRemote,
|
|
@@ -13467,6 +13610,7 @@ export {
|
|
|
13467
13610
|
secondsSinceSync,
|
|
13468
13611
|
syncStatusLabel,
|
|
13469
13612
|
pendingChanges,
|
|
13613
|
+
boardWebUrl,
|
|
13470
13614
|
syncMessage,
|
|
13471
13615
|
effectiveAutosync,
|
|
13472
13616
|
clearPushLock,
|