brainfile 0.20.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.
Files changed (90) hide show
  1. package/dist/{chunk-UOCQBEEW.mjs → chunk-MUV7XTFA.mjs} +1345 -316
  2. package/dist/cli.mjs +2196 -1349
  3. package/dist/commands/brief.d.ts +1 -0
  4. package/dist/commands/brief.d.ts.map +1 -1
  5. package/dist/commands/brief.js +10 -0
  6. package/dist/commands/brief.js.map +1 -1
  7. package/dist/commands/init.d.ts +14 -0
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +115 -15
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/commands/log.d.ts.map +1 -1
  12. package/dist/commands/log.js +4 -1
  13. package/dist/commands/log.js.map +1 -1
  14. package/dist/commands/mcp.d.ts.map +1 -1
  15. package/dist/commands/mcp.js +44 -0
  16. package/dist/commands/mcp.js.map +1 -1
  17. package/dist/commands/merge-driver.d.ts +26 -0
  18. package/dist/commands/merge-driver.d.ts.map +1 -0
  19. package/dist/commands/merge-driver.js +195 -0
  20. package/dist/commands/merge-driver.js.map +1 -0
  21. package/dist/commands/migrate-tracked.d.ts +8 -0
  22. package/dist/commands/migrate-tracked.d.ts.map +1 -0
  23. package/dist/commands/migrate-tracked.js +428 -0
  24. package/dist/commands/migrate-tracked.js.map +1 -0
  25. package/dist/commands/migrate.d.ts +6 -0
  26. package/dist/commands/migrate.d.ts.map +1 -1
  27. package/dist/commands/migrate.js +9 -0
  28. package/dist/commands/migrate.js.map +1 -1
  29. package/dist/commands/sync.d.ts +18 -0
  30. package/dist/commands/sync.d.ts.map +1 -0
  31. package/dist/commands/sync.js +161 -0
  32. package/dist/commands/sync.js.map +1 -0
  33. package/dist/commands/where.d.ts +24 -0
  34. package/dist/commands/where.d.ts.map +1 -0
  35. package/dist/commands/where.js +141 -0
  36. package/dist/commands/where.js.map +1 -0
  37. package/dist/mcp/helpers.d.ts +9 -0
  38. package/dist/mcp/helpers.d.ts.map +1 -1
  39. package/dist/mcp/helpers.js +43 -0
  40. package/dist/mcp/helpers.js.map +1 -1
  41. package/dist/mcp/tools/brief_tool.d.ts.map +1 -1
  42. package/dist/mcp/tools/brief_tool.js +39 -1
  43. package/dist/mcp/tools/brief_tool.js.map +1 -1
  44. package/dist/mcp/tools/contract_tool.d.ts.map +1 -1
  45. package/dist/mcp/tools/contract_tool.js +6 -5
  46. package/dist/mcp/tools/contract_tool.js.map +1 -1
  47. package/dist/mcp/tools/subtask_tool.d.ts.map +1 -1
  48. package/dist/mcp/tools/subtask_tool.js +5 -4
  49. package/dist/mcp/tools/subtask_tool.js.map +1 -1
  50. package/dist/mcp/tools/task_add_tool.d.ts.map +1 -1
  51. package/dist/mcp/tools/task_add_tool.js +6 -5
  52. package/dist/mcp/tools/task_add_tool.js.map +1 -1
  53. package/dist/mcp/tools/task_complete_tool.d.ts.map +1 -1
  54. package/dist/mcp/tools/task_complete_tool.js +5 -4
  55. package/dist/mcp/tools/task_complete_tool.js.map +1 -1
  56. package/dist/mcp/tools/task_delete_tool.d.ts.map +1 -1
  57. package/dist/mcp/tools/task_delete_tool.js +5 -4
  58. package/dist/mcp/tools/task_delete_tool.js.map +1 -1
  59. package/dist/mcp/tools/task_move_tool.d.ts.map +1 -1
  60. package/dist/mcp/tools/task_move_tool.js +6 -5
  61. package/dist/mcp/tools/task_move_tool.js.map +1 -1
  62. package/dist/mcp/tools/task_patch_tool.d.ts.map +1 -1
  63. package/dist/mcp/tools/task_patch_tool.js +5 -4
  64. package/dist/mcp/tools/task_patch_tool.js.map +1 -1
  65. package/dist/{tui-H3RI22S4.mjs → tui-5TFQR53I.mjs} +295 -253
  66. package/dist/utils/actor.d.ts +22 -0
  67. package/dist/utils/actor.d.ts.map +1 -0
  68. package/dist/utils/actor.js +200 -0
  69. package/dist/utils/actor.js.map +1 -0
  70. package/dist/utils/board-autosync.d.ts +22 -0
  71. package/dist/utils/board-autosync.d.ts.map +1 -0
  72. package/dist/utils/board-autosync.js +166 -0
  73. package/dist/utils/board-autosync.js.map +1 -0
  74. package/dist/utils/board-commit-hooks.d.ts +12 -0
  75. package/dist/utils/board-commit-hooks.d.ts.map +1 -0
  76. package/dist/utils/board-commit-hooks.js +52 -0
  77. package/dist/utils/board-commit-hooks.js.map +1 -0
  78. package/dist/utils/board-repo.d.ts +132 -0
  79. package/dist/utils/board-repo.d.ts.map +1 -0
  80. package/dist/utils/board-repo.js +602 -0
  81. package/dist/utils/board-repo.js.map +1 -0
  82. package/dist/utils/board-sync.d.ts +126 -0
  83. package/dist/utils/board-sync.d.ts.map +1 -0
  84. package/dist/utils/board-sync.js +554 -0
  85. package/dist/utils/board-sync.js.map +1 -0
  86. package/dist/utils/brainfile-path.d.ts +11 -1
  87. package/dist/utils/brainfile-path.d.ts.map +1 -1
  88. package/dist/utils/brainfile-path.js +39 -2
  89. package/dist/utils/brainfile-path.js.map +1 -1
  90. package/package.json +2 -2
@@ -880,15 +880,15 @@ var require_route = __commonJS({
880
880
  };
881
881
  }
882
882
  function wrapConversion(toModel, graph) {
883
- const path7 = [graph[toModel].parent, toModel];
883
+ const path11 = [graph[toModel].parent, toModel];
884
884
  let fn = conversions[graph[toModel].parent][toModel];
885
885
  let cur = graph[toModel].parent;
886
886
  while (graph[cur].parent) {
887
- path7.unshift(graph[cur].parent);
887
+ path11.unshift(graph[cur].parent);
888
888
  fn = link(conversions[graph[cur].parent][cur], fn);
889
889
  cur = graph[cur].parent;
890
890
  }
891
- fn.conversion = path7;
891
+ fn.conversion = path11;
892
892
  return fn;
893
893
  }
894
894
  module.exports = function(fromModel) {
@@ -1128,7 +1128,7 @@ var require_has_flag = __commonJS({
1128
1128
  var require_supports_color = __commonJS({
1129
1129
  "../node_modules/supports-color/index.js"(exports, module) {
1130
1130
  "use strict";
1131
- var os2 = __require("os");
1131
+ var os5 = __require("os");
1132
1132
  var tty = __require("tty");
1133
1133
  var hasFlag = require_has_flag();
1134
1134
  var { env } = process;
@@ -1176,7 +1176,7 @@ var require_supports_color = __commonJS({
1176
1176
  return min;
1177
1177
  }
1178
1178
  if (process.platform === "win32") {
1179
- const osRelease = os2.release().split(".");
1179
+ const osRelease = os5.release().split(".");
1180
1180
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
1181
1181
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
1182
1182
  }
@@ -2086,12 +2086,12 @@ var require_contractSpec = __commonJS({
2086
2086
  const type = raw.slice(0, firstColon).trim();
2087
2087
  const rest = raw.slice(firstColon + 1);
2088
2088
  const secondColon = rest.indexOf(":");
2089
- const path7 = (secondColon === -1 ? rest : rest.slice(0, secondColon)).trim();
2089
+ const path11 = (secondColon === -1 ? rest : rest.slice(0, secondColon)).trim();
2090
2090
  const description = (secondColon === -1 ? "" : rest.slice(secondColon + 1)).trim();
2091
2091
  if (!type) {
2092
2092
  throw new Error(`Invalid deliverable format: "${spec}". Expected "type:path:description" with non-empty type.`);
2093
2093
  }
2094
- if (!path7) {
2094
+ if (!path11) {
2095
2095
  throw new Error(`Invalid deliverable format: "${spec}". Expected "type:path:description" with non-empty path.`);
2096
2096
  }
2097
2097
  const typeLower = type.toLowerCase();
@@ -2100,7 +2100,7 @@ var require_contractSpec = __commonJS({
2100
2100
  }
2101
2101
  const deliverable = {
2102
2102
  type: typeLower,
2103
- path: path7,
2103
+ path: path11,
2104
2104
  ...description ? { description } : {}
2105
2105
  };
2106
2106
  return deliverable;
@@ -5570,6 +5570,104 @@ ${yamlContent}---
5570
5570
  }
5571
5571
  });
5572
5572
 
5573
+ // ../core/dist/frontmatter.js
5574
+ var require_frontmatter = __commonJS({
5575
+ "../core/dist/frontmatter.js"(exports) {
5576
+ "use strict";
5577
+ var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
5578
+ if (k2 === void 0) k2 = k;
5579
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5580
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
5581
+ desc = { enumerable: true, get: function() {
5582
+ return m[k];
5583
+ } };
5584
+ }
5585
+ Object.defineProperty(o, k2, desc);
5586
+ }) : (function(o, m, k, k2) {
5587
+ if (k2 === void 0) k2 = k;
5588
+ o[k2] = m[k];
5589
+ }));
5590
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
5591
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
5592
+ }) : function(o, v) {
5593
+ o["default"] = v;
5594
+ });
5595
+ var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
5596
+ var ownKeys = function(o) {
5597
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
5598
+ var ar = [];
5599
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
5600
+ return ar;
5601
+ };
5602
+ return ownKeys(o);
5603
+ };
5604
+ return function(mod) {
5605
+ if (mod && mod.__esModule) return mod;
5606
+ var result = {};
5607
+ if (mod != null) {
5608
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
5609
+ }
5610
+ __setModuleDefault(result, mod);
5611
+ return result;
5612
+ };
5613
+ })();
5614
+ Object.defineProperty(exports, "__esModule", { value: true });
5615
+ exports.parseFrontmatter = parseFrontmatter2;
5616
+ exports.serializeFrontmatter = serializeFrontmatter2;
5617
+ var yaml = __importStar(require_js_yaml());
5618
+ function parseFrontmatter2(content) {
5619
+ const lines = content.split("\n");
5620
+ if (!lines[0] || lines[0].trim() !== "---") {
5621
+ return null;
5622
+ }
5623
+ let endIndex = -1;
5624
+ for (let i = 1; i < lines.length; i++) {
5625
+ if (lines[i].trim() === "---") {
5626
+ endIndex = i;
5627
+ break;
5628
+ }
5629
+ }
5630
+ if (endIndex === -1) {
5631
+ return null;
5632
+ }
5633
+ const yamlContent = lines.slice(1, endIndex).join("\n");
5634
+ const bodyContent = lines.slice(endIndex + 1).join("\n");
5635
+ let parsed;
5636
+ try {
5637
+ parsed = yaml.load(yamlContent);
5638
+ } catch {
5639
+ return null;
5640
+ }
5641
+ if (!parsed || typeof parsed !== "object") {
5642
+ return null;
5643
+ }
5644
+ return {
5645
+ data: parsed,
5646
+ body: bodyContent.replace(/^\n/, "")
5647
+ };
5648
+ }
5649
+ function serializeFrontmatter2(data, body = "") {
5650
+ const yamlContent = yaml.dump(data, {
5651
+ indent: 2,
5652
+ lineWidth: -1,
5653
+ noRefs: true,
5654
+ sortKeys: false,
5655
+ quotingType: '"',
5656
+ forceQuotes: false
5657
+ });
5658
+ const parts = ["---\n", yamlContent, "---\n"];
5659
+ if (body.length > 0) {
5660
+ parts.push("\n");
5661
+ parts.push(body);
5662
+ if (!body.endsWith("\n")) {
5663
+ parts.push("\n");
5664
+ }
5665
+ }
5666
+ return parts.join("");
5667
+ }
5668
+ }
5669
+ });
5670
+
5573
5671
  // ../core/dist/validator.js
5574
5672
  var require_validator = __commonJS({
5575
5673
  "../core/dist/validator.js"(exports) {
@@ -5630,23 +5728,23 @@ var require_validator = __commonJS({
5630
5728
  * @param path - The path for error reporting
5631
5729
  * @returns Array of validation errors
5632
5730
  */
5633
- static validateColumn(column, path7) {
5731
+ static validateColumn(column, path11) {
5634
5732
  const errors = [];
5635
5733
  if (!column) {
5636
- errors.push({ path: path7, message: "Column is null or undefined" });
5734
+ errors.push({ path: path11, message: "Column is null or undefined" });
5637
5735
  return errors;
5638
5736
  }
5639
5737
  if (typeof column.id !== "string" || column.id.trim() === "") {
5640
- errors.push({ path: `${path7}.id`, message: "Column id must be a non-empty string" });
5738
+ errors.push({ path: `${path11}.id`, message: "Column id must be a non-empty string" });
5641
5739
  }
5642
5740
  if (typeof column.title !== "string" || column.title.trim() === "") {
5643
- errors.push({ path: `${path7}.title`, message: "Column title must be a non-empty string" });
5741
+ errors.push({ path: `${path11}.title`, message: "Column title must be a non-empty string" });
5644
5742
  }
5645
5743
  if (column.tasks !== void 0 && !Array.isArray(column.tasks)) {
5646
- errors.push({ path: `${path7}.tasks`, message: "Column tasks must be an array" });
5744
+ errors.push({ path: `${path11}.tasks`, message: "Column tasks must be an array" });
5647
5745
  } else if (Array.isArray(column.tasks)) {
5648
5746
  column.tasks.forEach((task, index) => {
5649
- const taskErrors = this.validateTask(task, `${path7}.tasks[${index}]`);
5747
+ const taskErrors = this.validateTask(task, `${path11}.tasks[${index}]`);
5650
5748
  errors.push(...taskErrors);
5651
5749
  });
5652
5750
  }
@@ -5658,23 +5756,23 @@ var require_validator = __commonJS({
5658
5756
  * @param path - The path for error reporting
5659
5757
  * @returns Array of validation errors
5660
5758
  */
5661
- static validateTask(task, path7) {
5759
+ static validateTask(task, path11) {
5662
5760
  const errors = [];
5663
5761
  if (!task) {
5664
- errors.push({ path: path7, message: "Task is null or undefined" });
5762
+ errors.push({ path: path11, message: "Task is null or undefined" });
5665
5763
  return errors;
5666
5764
  }
5667
5765
  if (typeof task.id !== "string" || task.id.trim() === "") {
5668
- errors.push({ path: `${path7}.id`, message: "Task id must be a non-empty string" });
5766
+ errors.push({ path: `${path11}.id`, message: "Task id must be a non-empty string" });
5669
5767
  }
5670
5768
  if (typeof task.title !== "string" || task.title.trim() === "") {
5671
- errors.push({ path: `${path7}.title`, message: "Task title must be a non-empty string" });
5769
+ errors.push({ path: `${path11}.title`, message: "Task title must be a non-empty string" });
5672
5770
  }
5673
5771
  if (task.priority !== void 0) {
5674
5772
  const validPriorities = ["low", "medium", "high", "critical"];
5675
5773
  if (!validPriorities.includes(task.priority)) {
5676
5774
  errors.push({
5677
- path: `${path7}.priority`,
5775
+ path: `${path11}.priority`,
5678
5776
  message: `Task priority must be one of: ${validPriorities.join(", ")}`
5679
5777
  });
5680
5778
  }
@@ -5683,23 +5781,23 @@ var require_validator = __commonJS({
5683
5781
  const validTemplates = ["bug", "feature", "refactor"];
5684
5782
  if (!validTemplates.includes(task.template)) {
5685
5783
  errors.push({
5686
- path: `${path7}.template`,
5784
+ path: `${path11}.template`,
5687
5785
  message: `Task template must be one of: ${validTemplates.join(", ")}`
5688
5786
  });
5689
5787
  }
5690
5788
  }
5691
5789
  if (task.tags !== void 0 && !Array.isArray(task.tags)) {
5692
- errors.push({ path: `${path7}.tags`, message: "Task tags must be an array" });
5790
+ errors.push({ path: `${path11}.tags`, message: "Task tags must be an array" });
5693
5791
  }
5694
5792
  if (task.relatedFiles !== void 0 && !Array.isArray(task.relatedFiles)) {
5695
- errors.push({ path: `${path7}.relatedFiles`, message: "Task relatedFiles must be an array" });
5793
+ errors.push({ path: `${path11}.relatedFiles`, message: "Task relatedFiles must be an array" });
5696
5794
  }
5697
5795
  if (task.subtasks !== void 0) {
5698
5796
  if (!Array.isArray(task.subtasks)) {
5699
- errors.push({ path: `${path7}.subtasks`, message: "Task subtasks must be an array" });
5797
+ errors.push({ path: `${path11}.subtasks`, message: "Task subtasks must be an array" });
5700
5798
  } else {
5701
5799
  task.subtasks.forEach((subtask, index) => {
5702
- const subtaskErrors = this.validateSubtask(subtask, `${path7}.subtasks[${index}]`);
5800
+ const subtaskErrors = this.validateSubtask(subtask, `${path11}.subtasks[${index}]`);
5703
5801
  errors.push(...subtaskErrors);
5704
5802
  });
5705
5803
  }
@@ -5712,20 +5810,20 @@ var require_validator = __commonJS({
5712
5810
  * @param path - The path for error reporting
5713
5811
  * @returns Array of validation errors
5714
5812
  */
5715
- static validateSubtask(subtask, path7) {
5813
+ static validateSubtask(subtask, path11) {
5716
5814
  const errors = [];
5717
5815
  if (!subtask) {
5718
- errors.push({ path: path7, message: "Subtask is null or undefined" });
5816
+ errors.push({ path: path11, message: "Subtask is null or undefined" });
5719
5817
  return errors;
5720
5818
  }
5721
5819
  if (typeof subtask.id !== "string" || subtask.id.trim() === "") {
5722
- errors.push({ path: `${path7}.id`, message: "Subtask id must be a non-empty string" });
5820
+ errors.push({ path: `${path11}.id`, message: "Subtask id must be a non-empty string" });
5723
5821
  }
5724
5822
  if (typeof subtask.title !== "string" || subtask.title.trim() === "") {
5725
- errors.push({ path: `${path7}.title`, message: "Subtask title must be a non-empty string" });
5823
+ errors.push({ path: `${path11}.title`, message: "Subtask title must be a non-empty string" });
5726
5824
  }
5727
5825
  if (typeof subtask.completed !== "boolean") {
5728
- errors.push({ path: `${path7}.completed`, message: "Subtask completed must be a boolean" });
5826
+ errors.push({ path: `${path11}.completed`, message: "Subtask completed must be a boolean" });
5729
5827
  }
5730
5828
  return errors;
5731
5829
  }
@@ -5788,32 +5886,32 @@ var require_validator = __commonJS({
5788
5886
  * @param path - The path for error reporting
5789
5887
  * @returns Array of validation errors
5790
5888
  */
5791
- static validateJournalEntry(entry, path7) {
5889
+ static validateJournalEntry(entry, path11) {
5792
5890
  const errors = [];
5793
5891
  if (!entry) {
5794
- errors.push({ path: path7, message: "Entry is null or undefined" });
5892
+ errors.push({ path: path11, message: "Entry is null or undefined" });
5795
5893
  return errors;
5796
5894
  }
5797
5895
  if (typeof entry.id !== "string" || entry.id.trim() === "") {
5798
- errors.push({ path: `${path7}.id`, message: "Entry id must be a non-empty string" });
5896
+ errors.push({ path: `${path11}.id`, message: "Entry id must be a non-empty string" });
5799
5897
  }
5800
5898
  if (typeof entry.title !== "string" || entry.title.trim() === "") {
5801
- errors.push({ path: `${path7}.title`, message: "Entry title must be a non-empty string" });
5899
+ errors.push({ path: `${path11}.title`, message: "Entry title must be a non-empty string" });
5802
5900
  }
5803
5901
  if (typeof entry.createdAt !== "string" || entry.createdAt.trim() === "") {
5804
- errors.push({ path: `${path7}.createdAt`, message: "Entry createdAt must be an ISO 8601 timestamp string" });
5902
+ errors.push({ path: `${path11}.createdAt`, message: "Entry createdAt must be an ISO 8601 timestamp string" });
5805
5903
  }
5806
5904
  if (entry.content !== void 0 && typeof entry.content !== "string") {
5807
- errors.push({ path: `${path7}.content`, message: "Entry content must be a string" });
5905
+ errors.push({ path: `${path11}.content`, message: "Entry content must be a string" });
5808
5906
  }
5809
5907
  if (entry.summary !== void 0 && typeof entry.summary !== "string") {
5810
- errors.push({ path: `${path7}.summary`, message: "Entry summary must be a string" });
5908
+ errors.push({ path: `${path11}.summary`, message: "Entry summary must be a string" });
5811
5909
  }
5812
5910
  if (entry.mood !== void 0 && typeof entry.mood !== "string") {
5813
- errors.push({ path: `${path7}.mood`, message: "Entry mood must be a string" });
5911
+ errors.push({ path: `${path11}.mood`, message: "Entry mood must be a string" });
5814
5912
  }
5815
5913
  if (entry.tags !== void 0 && !Array.isArray(entry.tags)) {
5816
- errors.push({ path: `${path7}.tags`, message: "Entry tags must be an array" });
5914
+ errors.push({ path: `${path11}.tags`, message: "Entry tags must be an array" });
5817
5915
  }
5818
5916
  return errors;
5819
5917
  }
@@ -5822,104 +5920,6 @@ var require_validator = __commonJS({
5822
5920
  }
5823
5921
  });
5824
5922
 
5825
- // ../core/dist/frontmatter.js
5826
- var require_frontmatter = __commonJS({
5827
- "../core/dist/frontmatter.js"(exports) {
5828
- "use strict";
5829
- var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
5830
- if (k2 === void 0) k2 = k;
5831
- var desc = Object.getOwnPropertyDescriptor(m, k);
5832
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
5833
- desc = { enumerable: true, get: function() {
5834
- return m[k];
5835
- } };
5836
- }
5837
- Object.defineProperty(o, k2, desc);
5838
- }) : (function(o, m, k, k2) {
5839
- if (k2 === void 0) k2 = k;
5840
- o[k2] = m[k];
5841
- }));
5842
- var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
5843
- Object.defineProperty(o, "default", { enumerable: true, value: v });
5844
- }) : function(o, v) {
5845
- o["default"] = v;
5846
- });
5847
- var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
5848
- var ownKeys = function(o) {
5849
- ownKeys = Object.getOwnPropertyNames || function(o2) {
5850
- var ar = [];
5851
- for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
5852
- return ar;
5853
- };
5854
- return ownKeys(o);
5855
- };
5856
- return function(mod) {
5857
- if (mod && mod.__esModule) return mod;
5858
- var result = {};
5859
- if (mod != null) {
5860
- for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
5861
- }
5862
- __setModuleDefault(result, mod);
5863
- return result;
5864
- };
5865
- })();
5866
- Object.defineProperty(exports, "__esModule", { value: true });
5867
- exports.parseFrontmatter = parseFrontmatter;
5868
- exports.serializeFrontmatter = serializeFrontmatter;
5869
- var yaml = __importStar(require_js_yaml());
5870
- function parseFrontmatter(content) {
5871
- const lines = content.split("\n");
5872
- if (!lines[0] || lines[0].trim() !== "---") {
5873
- return null;
5874
- }
5875
- let endIndex = -1;
5876
- for (let i = 1; i < lines.length; i++) {
5877
- if (lines[i].trim() === "---") {
5878
- endIndex = i;
5879
- break;
5880
- }
5881
- }
5882
- if (endIndex === -1) {
5883
- return null;
5884
- }
5885
- const yamlContent = lines.slice(1, endIndex).join("\n");
5886
- const bodyContent = lines.slice(endIndex + 1).join("\n");
5887
- let parsed;
5888
- try {
5889
- parsed = yaml.load(yamlContent);
5890
- } catch {
5891
- return null;
5892
- }
5893
- if (!parsed || typeof parsed !== "object") {
5894
- return null;
5895
- }
5896
- return {
5897
- data: parsed,
5898
- body: bodyContent.replace(/^\n/, "")
5899
- };
5900
- }
5901
- function serializeFrontmatter(data, body = "") {
5902
- const yamlContent = yaml.dump(data, {
5903
- indent: 2,
5904
- lineWidth: -1,
5905
- noRefs: true,
5906
- sortKeys: false,
5907
- quotingType: '"',
5908
- forceQuotes: false
5909
- });
5910
- const parts = ["---\n", yamlContent, "---\n"];
5911
- if (body.length > 0) {
5912
- parts.push("\n");
5913
- parts.push(body);
5914
- if (!body.endsWith("\n")) {
5915
- parts.push("\n");
5916
- }
5917
- }
5918
- return parts.join("");
5919
- }
5920
- }
5921
- });
5922
-
5923
5923
  // ../core/dist/linter.js
5924
5924
  var require_linter = __commonJS({
5925
5925
  "../core/dist/linter.js"(exports) {
@@ -7443,8 +7443,8 @@ var require_discovery = __commonJS({
7443
7443
  exports.findPrimaryBrainfile = findPrimaryBrainfile;
7444
7444
  exports.findNearestBrainfile = findNearestBrainfile;
7445
7445
  exports.watchBrainfiles = watchBrainfiles;
7446
- var fs7 = __importStar(__require("fs"));
7447
- var path7 = __importStar(__require("path"));
7446
+ var fs11 = __importStar(__require("fs"));
7447
+ var path11 = __importStar(__require("path"));
7448
7448
  var parser_1 = require_parser();
7449
7449
  var query_1 = require_query();
7450
7450
  exports.BRAINFILE_PATTERNS = [
@@ -7480,7 +7480,7 @@ var require_discovery = __commonJS({
7480
7480
  "vendor"
7481
7481
  ];
7482
7482
  function isBrainfileName(filename) {
7483
- const name = path7.basename(filename).toLowerCase();
7483
+ const name = path11.basename(filename).toLowerCase();
7484
7484
  if (name === "brainfile.md" || name === ".brainfile.md" || name === ".bb.md") {
7485
7485
  return true;
7486
7486
  }
@@ -7490,11 +7490,11 @@ var require_discovery = __commonJS({
7490
7490
  return false;
7491
7491
  }
7492
7492
  function shouldExclude(filePath, excludeDirs) {
7493
- const parts = filePath.split(path7.sep);
7493
+ const parts = filePath.split(path11.sep);
7494
7494
  return parts.some((part) => excludeDirs.includes(part));
7495
7495
  }
7496
7496
  function extractBrainfileSuffix(filename) {
7497
- const name = path7.basename(filename).toLowerCase();
7497
+ const name = path11.basename(filename).toLowerCase();
7498
7498
  if (name.startsWith("brainfile.") && name.endsWith(".md") && name !== "brainfile.md") {
7499
7499
  const withoutPrefix = name.slice("brainfile.".length);
7500
7500
  const suffix = withoutPrefix.slice(0, -".md".length);
@@ -7514,10 +7514,10 @@ var require_discovery = __commonJS({
7514
7514
  }
7515
7515
  function parseFileMetadata(absolutePath, relativePath) {
7516
7516
  try {
7517
- const content = fs7.readFileSync(absolutePath, "utf-8");
7517
+ const content = fs11.readFileSync(absolutePath, "utf-8");
7518
7518
  const board = parser_1.BrainfileParser.parse(content);
7519
- const stats = fs7.statSync(absolutePath);
7520
- const filename = path7.basename(absolutePath);
7519
+ const stats = fs11.statSync(absolutePath);
7520
+ const filename = path11.basename(absolutePath);
7521
7521
  const isHidden = filename.startsWith(".");
7522
7522
  if (board) {
7523
7523
  return {
@@ -7551,10 +7551,10 @@ var require_discovery = __commonJS({
7551
7551
  return results;
7552
7552
  }
7553
7553
  try {
7554
- const entries = fs7.readdirSync(dir, { withFileTypes: true });
7554
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
7555
7555
  for (const entry of entries) {
7556
- const fullPath = path7.join(dir, entry.name);
7557
- const relativePath = path7.relative(rootDir, fullPath);
7556
+ const fullPath = path11.join(dir, entry.name);
7557
+ const relativePath = path11.relative(rootDir, fullPath);
7558
7558
  if (entry.isDirectory()) {
7559
7559
  if (shouldExclude(entry.name, options.excludeDirs)) {
7560
7560
  continue;
@@ -7586,11 +7586,11 @@ var require_discovery = __commonJS({
7586
7586
  maxDepth: options.maxDepth ?? 10,
7587
7587
  excludeDirs: options.excludeDirs ?? [...exports.EXCLUDE_DIRS]
7588
7588
  };
7589
- const absoluteRoot = path7.resolve(rootDir);
7589
+ const absoluteRoot = path11.resolve(rootDir);
7590
7590
  const files = findBrainfilesRecursive(absoluteRoot, absoluteRoot, opts);
7591
7591
  files.sort((a, b) => {
7592
- const aDepth = a.relativePath.split(path7.sep).length;
7593
- const bDepth = b.relativePath.split(path7.sep).length;
7592
+ const aDepth = a.relativePath.split(path11.sep).length;
7593
+ const bDepth = b.relativePath.split(path11.sep).length;
7594
7594
  if (aDepth !== bDepth) {
7595
7595
  return aDepth - bDepth;
7596
7596
  }
@@ -7604,17 +7604,17 @@ var require_discovery = __commonJS({
7604
7604
  };
7605
7605
  }
7606
7606
  function findPrimaryBrainfile(rootDir) {
7607
- const absoluteRoot = path7.resolve(rootDir);
7608
- const preferred = path7.join(absoluteRoot, ".brainfile", "brainfile.md");
7609
- if (fs7.existsSync(preferred)) {
7610
- const metadata = parseFileMetadata(preferred, path7.join(".brainfile", "brainfile.md"));
7607
+ const absoluteRoot = path11.resolve(rootDir);
7608
+ const preferred = path11.join(absoluteRoot, ".brainfile", "brainfile.md");
7609
+ if (fs11.existsSync(preferred)) {
7610
+ const metadata = parseFileMetadata(preferred, path11.join(".brainfile", "brainfile.md"));
7611
7611
  if (metadata)
7612
7612
  return metadata;
7613
7613
  }
7614
7614
  const priorityNames = ["brainfile.md", ".brainfile.md", ".bb.md"];
7615
7615
  for (const name of priorityNames) {
7616
- const fullPath = path7.join(absoluteRoot, name);
7617
- if (fs7.existsSync(fullPath)) {
7616
+ const fullPath = path11.join(absoluteRoot, name);
7617
+ if (fs11.existsSync(fullPath)) {
7618
7618
  const metadata = parseFileMetadata(fullPath, name);
7619
7619
  if (metadata) {
7620
7620
  return metadata;
@@ -7622,11 +7622,11 @@ var require_discovery = __commonJS({
7622
7622
  }
7623
7623
  }
7624
7624
  try {
7625
- const entries = fs7.readdirSync(absoluteRoot);
7625
+ const entries = fs11.readdirSync(absoluteRoot);
7626
7626
  for (const entry of entries) {
7627
7627
  if (isBrainfileName(entry) && !priorityNames.includes(entry.toLowerCase())) {
7628
- const fullPath = path7.join(absoluteRoot, entry);
7629
- const stats = fs7.statSync(fullPath);
7628
+ const fullPath = path11.join(absoluteRoot, entry);
7629
+ const stats = fs11.statSync(fullPath);
7630
7630
  if (stats.isFile()) {
7631
7631
  const metadata = parseFileMetadata(fullPath, entry);
7632
7632
  if (metadata) {
@@ -7640,14 +7640,14 @@ var require_discovery = __commonJS({
7640
7640
  return null;
7641
7641
  }
7642
7642
  function findNearestBrainfile(startDir) {
7643
- let currentDir = path7.resolve(startDir || process.cwd());
7644
- const root = path7.parse(currentDir).root;
7643
+ let currentDir = path11.resolve(startDir || process.cwd());
7644
+ const root = path11.parse(currentDir).root;
7645
7645
  while (currentDir !== root) {
7646
7646
  const found = findPrimaryBrainfile(currentDir);
7647
7647
  if (found) {
7648
7648
  return found;
7649
7649
  }
7650
- const parentDir = path7.dirname(currentDir);
7650
+ const parentDir = path11.dirname(currentDir);
7651
7651
  if (parentDir === currentDir) {
7652
7652
  break;
7653
7653
  }
@@ -7660,11 +7660,11 @@ var require_discovery = __commonJS({
7660
7660
  return null;
7661
7661
  }
7662
7662
  function watchBrainfiles(rootDir, callback, onError) {
7663
- const absoluteRoot = path7.resolve(rootDir);
7663
+ const absoluteRoot = path11.resolve(rootDir);
7664
7664
  let watcher = null;
7665
7665
  let active = false;
7666
7666
  try {
7667
- const stats = fs7.statSync(absoluteRoot);
7667
+ const stats = fs11.statSync(absoluteRoot);
7668
7668
  if (!stats.isDirectory()) {
7669
7669
  return {
7670
7670
  success: false,
@@ -7693,13 +7693,13 @@ var require_discovery = __commonJS({
7693
7693
  };
7694
7694
  }
7695
7695
  try {
7696
- watcher = fs7.watch(absoluteRoot, (eventType, filename) => {
7696
+ watcher = fs11.watch(absoluteRoot, (eventType, filename) => {
7697
7697
  if (!active || !filename || !isBrainfileName(filename))
7698
7698
  return;
7699
- const fullPath = path7.join(absoluteRoot, filename);
7699
+ const fullPath = path11.join(absoluteRoot, filename);
7700
7700
  try {
7701
7701
  if (eventType === "rename") {
7702
- if (fs7.existsSync(fullPath)) {
7702
+ if (fs11.existsSync(fullPath)) {
7703
7703
  const metadata = parseFileMetadata(fullPath, filename);
7704
7704
  if (metadata) {
7705
7705
  callback("add", metadata);
@@ -7815,8 +7815,8 @@ var require_files = __commonJS({
7815
7815
  exports.ensureDotBrainfileDir = ensureDotBrainfileDir;
7816
7816
  exports.ensureDotBrainfileGitignore = ensureDotBrainfileGitignore;
7817
7817
  exports.atomicWriteFileSync = atomicWriteFileSync;
7818
- var fs7 = __importStar(__require("fs"));
7819
- var path7 = __importStar(__require("path"));
7818
+ var fs11 = __importStar(__require("fs"));
7819
+ var path11 = __importStar(__require("path"));
7820
7820
  exports.DOT_BRAINFILE_DIRNAME = ".brainfile";
7821
7821
  exports.BRAINFILE_BASENAME = "brainfile.md";
7822
7822
  exports.BRAINFILE_STATE_BASENAME = "state.json";
@@ -7824,38 +7824,43 @@ var require_files = __commonJS({
7824
7824
  exports.DOT_BRAINFILE_STATE_DIRNAME = "state";
7825
7825
  exports.DOT_BRAINFILE_STATE_IGNORE_ENTRY = "state/";
7826
7826
  function toAbsolute(p) {
7827
- return path7.isAbsolute(p) ? p : path7.resolve(p);
7827
+ return path11.isAbsolute(p) ? p : path11.resolve(p);
7828
7828
  }
7829
7829
  function existsFile2(p) {
7830
7830
  try {
7831
- return fs7.statSync(p).isFile();
7831
+ return fs11.statSync(p).isFile();
7832
7832
  } catch {
7833
7833
  return false;
7834
7834
  }
7835
7835
  }
7836
- function findBrainfile2(startDir = process.cwd()) {
7837
- let currentDir = path7.resolve(startDir);
7838
- const root = path7.parse(currentDir).root;
7836
+ function isGitBoundary(dir) {
7837
+ return fs11.existsSync(path11.join(dir, ".git"));
7838
+ }
7839
+ function findBrainfile2(startDir = process.cwd(), options = {}) {
7840
+ let currentDir = path11.resolve(startDir);
7841
+ const root = path11.parse(currentDir).root;
7839
7842
  while (true) {
7840
- const preferred = path7.join(currentDir, exports.DOT_BRAINFILE_DIRNAME, exports.BRAINFILE_BASENAME);
7843
+ const preferred = path11.join(currentDir, exports.DOT_BRAINFILE_DIRNAME, exports.BRAINFILE_BASENAME);
7841
7844
  if (existsFile2(preferred)) {
7842
7845
  return { absolutePath: preferred, projectRoot: currentDir, kind: "dotdir" };
7843
7846
  }
7844
- const legacy = path7.join(currentDir, exports.BRAINFILE_BASENAME);
7847
+ const legacy = path11.join(currentDir, exports.BRAINFILE_BASENAME);
7845
7848
  if (existsFile2(legacy)) {
7846
7849
  return { absolutePath: legacy, projectRoot: currentDir, kind: "root" };
7847
7850
  }
7848
- const hiddenLegacy = path7.join(currentDir, ".brainfile.md");
7851
+ const hiddenLegacy = path11.join(currentDir, ".brainfile.md");
7849
7852
  if (existsFile2(hiddenLegacy)) {
7850
7853
  return { absolutePath: hiddenLegacy, projectRoot: currentDir, kind: "hidden" };
7851
7854
  }
7852
- const bbLegacy = path7.join(currentDir, ".bb.md");
7855
+ const bbLegacy = path11.join(currentDir, ".bb.md");
7853
7856
  if (existsFile2(bbLegacy)) {
7854
7857
  return { absolutePath: bbLegacy, projectRoot: currentDir, kind: "bb" };
7855
7858
  }
7859
+ if (options.stopAtGitRoot && isGitBoundary(currentDir))
7860
+ break;
7856
7861
  if (currentDir === root)
7857
7862
  break;
7858
- const parent = path7.dirname(currentDir);
7863
+ const parent = path11.dirname(currentDir);
7859
7864
  if (parent === currentDir)
7860
7865
  break;
7861
7866
  currentDir = parent;
@@ -7863,60 +7868,60 @@ var require_files = __commonJS({
7863
7868
  return null;
7864
7869
  }
7865
7870
  function resolveBrainfilePath2(options = {}) {
7866
- const startDir = options.startDir ? path7.resolve(options.startDir) : process.cwd();
7871
+ const startDir = options.startDir ? path11.resolve(options.startDir) : process.cwd();
7867
7872
  const filePath = options.filePath;
7868
7873
  const isDefaultPlaceholder = filePath === void 0 || filePath === exports.BRAINFILE_BASENAME || filePath === `./${exports.BRAINFILE_BASENAME}`;
7869
7874
  if (isDefaultPlaceholder) {
7870
- const found = findBrainfile2(startDir);
7875
+ const found = findBrainfile2(startDir, { stopAtGitRoot: options.stopAtGitRoot });
7871
7876
  if (found)
7872
7877
  return found.absolutePath;
7873
7878
  return toAbsolute(filePath ?? exports.BRAINFILE_BASENAME);
7874
7879
  }
7875
- return toAbsolute(path7.isAbsolute(filePath) ? filePath : path7.resolve(startDir, filePath));
7880
+ return toAbsolute(path11.isAbsolute(filePath) ? filePath : path11.resolve(startDir, filePath));
7876
7881
  }
7877
7882
  function getBrainfileStateDir(brainfilePath) {
7878
7883
  const abs = toAbsolute(brainfilePath);
7879
- const dir = path7.dirname(abs);
7880
- if (path7.basename(dir) === exports.DOT_BRAINFILE_DIRNAME)
7884
+ const dir = path11.dirname(abs);
7885
+ if (path11.basename(dir) === exports.DOT_BRAINFILE_DIRNAME)
7881
7886
  return dir;
7882
- return path7.join(dir, exports.DOT_BRAINFILE_DIRNAME);
7887
+ return path11.join(dir, exports.DOT_BRAINFILE_DIRNAME);
7883
7888
  }
7884
7889
  function getBrainfileStatePath(brainfilePath) {
7885
- return path7.join(getBrainfileStateDir(brainfilePath), exports.BRAINFILE_STATE_BASENAME);
7890
+ return path11.join(getBrainfileStateDir(brainfilePath), exports.BRAINFILE_STATE_BASENAME);
7886
7891
  }
7887
7892
  function getDotBrainfileGitignorePath(brainfilePath) {
7888
- return path7.join(getBrainfileStateDir(brainfilePath), exports.DOT_BRAINFILE_GITIGNORE_BASENAME);
7893
+ return path11.join(getBrainfileStateDir(brainfilePath), exports.DOT_BRAINFILE_GITIGNORE_BASENAME);
7889
7894
  }
7890
7895
  function ensureDotBrainfileDir(brainfilePath) {
7891
7896
  const dir = getBrainfileStateDir(brainfilePath);
7892
- fs7.mkdirSync(dir, { recursive: true });
7897
+ fs11.mkdirSync(dir, { recursive: true });
7893
7898
  return dir;
7894
7899
  }
7895
7900
  function ensureDotBrainfileGitignore(brainfilePath) {
7896
7901
  ensureDotBrainfileDir(brainfilePath);
7897
7902
  const gitignorePath = getDotBrainfileGitignorePath(brainfilePath);
7898
- const existing = fs7.existsSync(gitignorePath) ? fs7.readFileSync(gitignorePath, "utf-8") : "";
7903
+ const existing = fs11.existsSync(gitignorePath) ? fs11.readFileSync(gitignorePath, "utf-8") : "";
7899
7904
  const lines = existing.split(/\r?\n/).filter((line) => line.trim() !== exports.BRAINFILE_STATE_BASENAME);
7900
7905
  const hasStateDir = lines.some((line) => line.trim() === exports.DOT_BRAINFILE_STATE_IGNORE_ENTRY);
7901
7906
  if (!hasStateDir) {
7902
7907
  lines.push(exports.DOT_BRAINFILE_STATE_IGNORE_ENTRY);
7903
7908
  }
7904
7909
  const filtered = lines.join("\n").trim();
7905
- fs7.writeFileSync(gitignorePath, filtered.length > 0 ? `${filtered}
7910
+ fs11.writeFileSync(gitignorePath, filtered.length > 0 ? `${filtered}
7906
7911
  ` : "", "utf-8");
7907
7912
  }
7908
7913
  function atomicWriteFileSync(filePath, content) {
7909
7914
  const abs = toAbsolute(filePath);
7910
- const dir = path7.dirname(abs);
7911
- fs7.mkdirSync(dir, { recursive: true });
7912
- const tmp = path7.join(dir, `.${path7.basename(abs)}.tmp-${process.pid}-${Date.now()}`);
7915
+ const dir = path11.dirname(abs);
7916
+ fs11.mkdirSync(dir, { recursive: true });
7917
+ const tmp = path11.join(dir, `.${path11.basename(abs)}.tmp-${process.pid}-${Date.now()}`);
7913
7918
  try {
7914
- fs7.writeFileSync(tmp, content, "utf-8");
7915
- fs7.renameSync(tmp, abs);
7919
+ fs11.writeFileSync(tmp, content, "utf-8");
7920
+ fs11.renameSync(tmp, abs);
7916
7921
  } catch (err) {
7917
7922
  try {
7918
- if (fs7.existsSync(tmp))
7919
- fs7.unlinkSync(tmp);
7923
+ if (fs11.existsSync(tmp))
7924
+ fs11.unlinkSync(tmp);
7920
7925
  } catch {
7921
7926
  }
7922
7927
  throw err;
@@ -8000,8 +8005,8 @@ var require_taskFile = __commonJS({
8000
8005
  exports.writeTaskFile = writeTaskFile;
8001
8006
  exports.readTasksDir = readTasksDir;
8002
8007
  exports.taskFileName = taskFileName;
8003
- var fs7 = __importStar(__require("fs"));
8004
- var path7 = __importStar(__require("path"));
8008
+ var fs11 = __importStar(__require("fs"));
8009
+ var path11 = __importStar(__require("path"));
8005
8010
  var taskContent_1 = require_taskContent();
8006
8011
  var taskContent_2 = require_taskContent();
8007
8012
  Object.defineProperty(exports, "parseTaskContent", { enumerable: true, get: function() {
@@ -8018,15 +8023,15 @@ var require_taskFile = __commonJS({
8018
8023
  if (trimmed.includes("/") || trimmed.includes("\\") || trimmed.includes("..")) {
8019
8024
  return true;
8020
8025
  }
8021
- if (path7.isAbsolute(trimmed)) {
8026
+ if (path11.isAbsolute(trimmed)) {
8022
8027
  return true;
8023
8028
  }
8024
- return path7.basename(trimmed) !== trimmed;
8029
+ return path11.basename(trimmed) !== trimmed;
8025
8030
  }
8026
8031
  function readTaskFile(filePath) {
8027
8032
  let content;
8028
8033
  try {
8029
- content = fs7.readFileSync(filePath, "utf-8");
8034
+ content = fs11.readFileSync(filePath, "utf-8");
8030
8035
  } catch {
8031
8036
  return null;
8032
8037
  }
@@ -8037,19 +8042,19 @@ var require_taskFile = __commonJS({
8037
8042
  return {
8038
8043
  task: parsed.task,
8039
8044
  body: parsed.body,
8040
- filePath: path7.resolve(filePath)
8045
+ filePath: path11.resolve(filePath)
8041
8046
  };
8042
8047
  }
8043
8048
  function writeTaskFile(filePath, task, body = "") {
8044
- const dir = path7.dirname(filePath);
8045
- fs7.mkdirSync(dir, { recursive: true });
8049
+ const dir = path11.dirname(filePath);
8050
+ fs11.mkdirSync(dir, { recursive: true });
8046
8051
  const content = (0, taskContent_1.serializeTaskContent)(task, body);
8047
- fs7.writeFileSync(filePath, content, "utf-8");
8052
+ fs11.writeFileSync(filePath, content, "utf-8");
8048
8053
  }
8049
8054
  function readTasksDir(dirPath) {
8050
8055
  let entries;
8051
8056
  try {
8052
- entries = fs7.readdirSync(dirPath, { withFileTypes: true });
8057
+ entries = fs11.readdirSync(dirPath, { withFileTypes: true });
8053
8058
  } catch {
8054
8059
  return [];
8055
8060
  }
@@ -8058,7 +8063,7 @@ var require_taskFile = __commonJS({
8058
8063
  if (!entry.isFile() || !entry.name.endsWith(".md")) {
8059
8064
  continue;
8060
8065
  }
8061
- const filePath = path7.join(dirPath, entry.name);
8066
+ const filePath = path11.join(dirPath, entry.name);
8062
8067
  const doc = readTaskFile(filePath);
8063
8068
  if (doc) {
8064
8069
  docs.push(doc);
@@ -8125,14 +8130,14 @@ var require_ledger2 = __commonJS({
8125
8130
  exports.queryLedger = queryLedger;
8126
8131
  exports.getFileHistory = getFileHistory;
8127
8132
  exports.getTaskContext = getTaskContext;
8128
- var fs7 = __importStar(__require("fs"));
8129
- var path7 = __importStar(__require("path"));
8133
+ var fs11 = __importStar(__require("fs"));
8134
+ var path11 = __importStar(__require("path"));
8130
8135
  var taskFile_1 = require_taskFile();
8131
8136
  var ledger_1 = require_ledger();
8132
8137
  var LEDGER_FILE_NAME = "ledger.jsonl";
8133
8138
  var LEGACY_WARNING_TRACKER = /* @__PURE__ */ new Set();
8134
8139
  function getLedgerPath(logsDir) {
8135
- return path7.join(logsDir, LEDGER_FILE_NAME);
8140
+ return path11.join(logsDir, LEDGER_FILE_NAME);
8136
8141
  }
8137
8142
  function normalizePathValue(value) {
8138
8143
  return value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
@@ -8350,7 +8355,7 @@ var require_ledger2 = __commonJS({
8350
8355
  return parsed;
8351
8356
  }
8352
8357
  function shouldWarnLegacyFallback(logsDir) {
8353
- const key = path7.resolve(logsDir);
8358
+ const key = path11.resolve(logsDir);
8354
8359
  if (LEGACY_WARNING_TRACKER.has(key)) {
8355
8360
  return false;
8356
8361
  }
@@ -8436,18 +8441,18 @@ var require_ledger2 = __commonJS({
8436
8441
  return record;
8437
8442
  }
8438
8443
  function appendLedgerRecord(logsDir, record) {
8439
- fs7.mkdirSync(logsDir, { recursive: true });
8444
+ fs11.mkdirSync(logsDir, { recursive: true });
8440
8445
  const ledgerPath = getLedgerPath(logsDir);
8441
- fs7.appendFileSync(ledgerPath, `${JSON.stringify(record)}
8446
+ fs11.appendFileSync(ledgerPath, `${JSON.stringify(record)}
8442
8447
  `, "utf-8");
8443
8448
  return ledgerPath;
8444
8449
  }
8445
8450
  function readLedger(logsDir) {
8446
8451
  const ledgerPath = getLedgerPath(logsDir);
8447
- if (!fs7.existsSync(ledgerPath)) {
8452
+ if (!fs11.existsSync(ledgerPath)) {
8448
8453
  return readLegacyMarkdownLedger(logsDir);
8449
8454
  }
8450
- const content = fs7.readFileSync(ledgerPath, "utf-8");
8455
+ const content = fs11.readFileSync(ledgerPath, "utf-8");
8451
8456
  const lines = content.split("\n");
8452
8457
  const records = [];
8453
8458
  for (let index = 0; index < lines.length; index++) {
@@ -8576,35 +8581,35 @@ var require_workspace = __commonJS({
8576
8581
  exports.composeBody = composeBody2;
8577
8582
  exports.readV2BoardConfig = readV2BoardConfig2;
8578
8583
  exports.buildBoardFromV2 = buildBoardFromV22;
8579
- var fs7 = __importStar(__require("fs"));
8580
- var path7 = __importStar(__require("path"));
8584
+ var fs11 = __importStar(__require("fs"));
8585
+ var path11 = __importStar(__require("path"));
8581
8586
  var parser_1 = require_parser();
8582
8587
  var taskFile_1 = require_taskFile();
8583
8588
  function getV2Dirs2(brainfilePath) {
8584
- const resolvedPath = path7.resolve(brainfilePath);
8585
- const dotDir = path7.dirname(resolvedPath);
8589
+ const resolvedPath = path11.resolve(brainfilePath);
8590
+ const dotDir = path11.dirname(resolvedPath);
8586
8591
  return {
8587
8592
  dotDir,
8588
- boardDir: path7.join(dotDir, "board"),
8589
- logsDir: path7.join(dotDir, "logs"),
8593
+ boardDir: path11.join(dotDir, "board"),
8594
+ logsDir: path11.join(dotDir, "logs"),
8590
8595
  brainfilePath: resolvedPath
8591
8596
  };
8592
8597
  }
8593
8598
  function isV25(brainfilePath) {
8594
8599
  const { boardDir } = getV2Dirs2(brainfilePath);
8595
- return fs7.existsSync(boardDir);
8600
+ return fs11.existsSync(boardDir);
8596
8601
  }
8597
8602
  function ensureV2Dirs2(brainfilePath) {
8598
8603
  const dirs = getV2Dirs2(brainfilePath);
8599
- fs7.mkdirSync(dirs.boardDir, { recursive: true });
8600
- fs7.mkdirSync(dirs.logsDir, { recursive: true });
8604
+ fs11.mkdirSync(dirs.boardDir, { recursive: true });
8605
+ fs11.mkdirSync(dirs.logsDir, { recursive: true });
8601
8606
  return dirs;
8602
8607
  }
8603
8608
  function getTaskFilePath2(boardDir, taskId) {
8604
- return path7.join(boardDir, (0, taskFile_1.taskFileName)(taskId));
8609
+ return path11.join(boardDir, (0, taskFile_1.taskFileName)(taskId));
8605
8610
  }
8606
8611
  function getLogFilePath2(logsDir, taskId) {
8607
- return path7.join(logsDir, (0, taskFile_1.taskFileName)(taskId));
8612
+ return path11.join(logsDir, (0, taskFile_1.taskFileName)(taskId));
8608
8613
  }
8609
8614
  function findV2Task2(dirs, taskId, searchLogs = false) {
8610
8615
  const taskPath = getTaskFilePath2(dirs.boardDir, taskId);
@@ -8665,7 +8670,7 @@ ${log.trim()}`);
8665
8670
  `;
8666
8671
  }
8667
8672
  function readV2BoardConfig2(brainfilePath) {
8668
- const content = fs7.readFileSync(brainfilePath, "utf-8");
8673
+ const content = fs11.readFileSync(brainfilePath, "utf-8");
8669
8674
  const result = parser_1.BrainfileParser.parseWithErrors(content);
8670
8675
  if (!result.board) {
8671
8676
  throw new Error(`Failed to parse brainfile: ${result.error}`);
@@ -8760,7 +8765,7 @@ var require_brief = __commonJS({
8760
8765
  exports.relativeTime = relativeTime;
8761
8766
  exports.buildBrief = buildBrief;
8762
8767
  exports.isEmptyBrief = isEmptyBrief;
8763
- var fs7 = __importStar(__require("fs"));
8768
+ var fs11 = __importStar(__require("fs"));
8764
8769
  var taskFile_1 = require_taskFile();
8765
8770
  var ledger_1 = require_ledger2();
8766
8771
  var workspace_1 = require_workspace();
@@ -8864,7 +8869,7 @@ var require_brief = __commonJS({
8864
8869
  }
8865
8870
  function boardMtimeMs(brainfilePath) {
8866
8871
  try {
8867
- return fs7.statSync(brainfilePath).mtimeMs;
8872
+ return fs11.statSync(brainfilePath).mtimeMs;
8868
8873
  } catch {
8869
8874
  return null;
8870
8875
  }
@@ -9011,13 +9016,13 @@ var require_brief = __commonJS({
9011
9016
  const isUpdated = updatedMs !== null && updatedMs > lastBriefAtMs;
9012
9017
  if (!isNew && !isUpdated)
9013
9018
  continue;
9014
- const stamp = isNew ? doc.task.createdAt : doc.task.updatedAt;
9019
+ const stamp2 = isNew ? doc.task.createdAt : doc.task.updatedAt;
9015
9020
  const verb = isNew ? "created" : "updated";
9016
9021
  changeItems.push({
9017
9022
  taskId: doc.task.id,
9018
9023
  text: doc.task.title,
9019
- why: `${verb} ${agoPhrase(stamp, generatedAt)} \xB7 ${taskStateWhy(doc.task)}`,
9020
- ...stamp ? { at: stamp } : {}
9024
+ why: `${verb} ${agoPhrase(stamp2, generatedAt)} \xB7 ${taskStateWhy(doc.task)}`,
9025
+ ...stamp2 ? { at: stamp2 } : {}
9021
9026
  });
9022
9027
  }
9023
9028
  const completionItems = completionsFor(dirs, agentName, lastBriefAtMs).map((record) => ({
@@ -9097,8 +9102,8 @@ var require_briefState = __commonJS({
9097
9102
  exports.getBriefStatePath = getBriefStatePath;
9098
9103
  exports.readBriefState = readBriefState;
9099
9104
  exports.writeBriefState = writeBriefState;
9100
- var fs7 = __importStar(__require("fs"));
9101
- var path7 = __importStar(__require("path"));
9105
+ var fs11 = __importStar(__require("fs"));
9106
+ var path11 = __importStar(__require("path"));
9102
9107
  var files_1 = require_files();
9103
9108
  exports.BRIEF_STATE_VERSION = 1;
9104
9109
  function sanitizeAgentFilename(agentName) {
@@ -9116,18 +9121,18 @@ var require_briefState = __commonJS({
9116
9121
  return cleaned;
9117
9122
  }
9118
9123
  function getBriefStateDir(brainfilePath) {
9119
- return path7.join((0, files_1.getBrainfileStateDir)(brainfilePath), files_1.DOT_BRAINFILE_STATE_DIRNAME);
9124
+ return path11.join((0, files_1.getBrainfileStateDir)(brainfilePath), files_1.DOT_BRAINFILE_STATE_DIRNAME);
9120
9125
  }
9121
9126
  function getBriefStatePath(brainfilePath, agentName) {
9122
- return path7.join(getBriefStateDir(brainfilePath), `${sanitizeAgentFilename(agentName)}.json`);
9127
+ return path11.join(getBriefStateDir(brainfilePath), `${sanitizeAgentFilename(agentName)}.json`);
9123
9128
  }
9124
9129
  function readBriefState(brainfilePath, agentName) {
9125
9130
  const statePath = getBriefStatePath(brainfilePath, agentName);
9126
9131
  const fallback = { version: exports.BRIEF_STATE_VERSION, agent: agentName, lastBriefAt: null };
9127
- if (!fs7.existsSync(statePath))
9132
+ if (!fs11.existsSync(statePath))
9128
9133
  return fallback;
9129
9134
  try {
9130
- const parsed = JSON.parse(fs7.readFileSync(statePath, "utf-8"));
9135
+ const parsed = JSON.parse(fs11.readFileSync(statePath, "utf-8"));
9131
9136
  const lastBriefAt = typeof parsed.lastBriefAt === "string" ? parsed.lastBriefAt : null;
9132
9137
  return {
9133
9138
  version: typeof parsed.version === "number" ? parsed.version : exports.BRIEF_STATE_VERSION,
@@ -9361,8 +9366,8 @@ var require_boardFile = __commonJS({
9361
9366
  exports.serializeBoardConfig = exports.parseBoardConfig = void 0;
9362
9367
  exports.readBoardConfig = readBoardConfig;
9363
9368
  exports.writeBoardConfig = writeBoardConfig;
9364
- var fs7 = __importStar(__require("fs"));
9365
- var path7 = __importStar(__require("path"));
9369
+ var fs11 = __importStar(__require("fs"));
9370
+ var path11 = __importStar(__require("path"));
9366
9371
  var boardContent_1 = require_boardContent();
9367
9372
  var boardContent_2 = require_boardContent();
9368
9373
  Object.defineProperty(exports, "parseBoardConfig", { enumerable: true, get: function() {
@@ -9374,7 +9379,7 @@ var require_boardFile = __commonJS({
9374
9379
  function readBoardConfig(filePath) {
9375
9380
  let content;
9376
9381
  try {
9377
- content = fs7.readFileSync(filePath, "utf-8");
9382
+ content = fs11.readFileSync(filePath, "utf-8");
9378
9383
  } catch {
9379
9384
  return null;
9380
9385
  }
@@ -9385,14 +9390,14 @@ var require_boardFile = __commonJS({
9385
9390
  return {
9386
9391
  config: parsed.config,
9387
9392
  body: parsed.body,
9388
- filePath: path7.resolve(filePath)
9393
+ filePath: path11.resolve(filePath)
9389
9394
  };
9390
9395
  }
9391
9396
  function writeBoardConfig(filePath, config, body = "") {
9392
- const dir = path7.dirname(filePath);
9393
- fs7.mkdirSync(dir, { recursive: true });
9397
+ const dir = path11.dirname(filePath);
9398
+ fs11.mkdirSync(dir, { recursive: true });
9394
9399
  const content = (0, boardContent_1.serializeBoardConfig)(config, body);
9395
- fs7.writeFileSync(filePath, content, "utf-8");
9400
+ fs11.writeFileSync(filePath, content, "utf-8");
9396
9401
  }
9397
9402
  }
9398
9403
  });
@@ -9464,8 +9469,8 @@ var require_taskOperations = __commonJS({
9464
9469
  exports.activateTaskContract = activateTaskContract;
9465
9470
  exports.activateTaskContractsByParent = activateTaskContractsByParent;
9466
9471
  exports.getEffectiveState = getEffectiveState;
9467
- var fs7 = __importStar(__require("fs"));
9468
- var path7 = __importStar(__require("path"));
9472
+ var fs11 = __importStar(__require("fs"));
9473
+ var path11 = __importStar(__require("path"));
9469
9474
  var boardValidation_1 = require_boardValidation();
9470
9475
  var contractSpec_1 = require_contractSpec();
9471
9476
  var idGen_1 = require_idGen();
@@ -9567,21 +9572,21 @@ ${section}
9567
9572
  return lines.join("\n");
9568
9573
  }
9569
9574
  function writeTaskFileExclusive(filePath, task, body) {
9570
- const dir = path7.dirname(filePath);
9571
- fs7.mkdirSync(dir, { recursive: true });
9575
+ const dir = path11.dirname(filePath);
9576
+ fs11.mkdirSync(dir, { recursive: true });
9572
9577
  const content = (0, taskFile_1.serializeTaskContent)(task, body);
9573
- fs7.writeFileSync(filePath, content, { encoding: "utf-8", flag: "wx" });
9578
+ fs11.writeFileSync(filePath, content, { encoding: "utf-8", flag: "wx" });
9574
9579
  }
9575
9580
  function rollbackLedgerAppend(logsDir, appendedRecord) {
9576
- const ledgerPath = path7.join(logsDir, "ledger.jsonl");
9581
+ const ledgerPath = path11.join(logsDir, "ledger.jsonl");
9577
9582
  const appendedLine = `${JSON.stringify(appendedRecord)}
9578
9583
  `;
9579
9584
  const appendedBytes = Buffer.byteLength(appendedLine, "utf-8");
9580
9585
  try {
9581
- const stat = fs7.statSync(ledgerPath);
9586
+ const stat = fs11.statSync(ledgerPath);
9582
9587
  const newSize = stat.size - appendedBytes;
9583
9588
  if (newSize >= 0) {
9584
- fs7.truncateSync(ledgerPath, newSize);
9589
+ fs11.truncateSync(ledgerPath, newSize);
9585
9590
  }
9586
9591
  } catch {
9587
9592
  }
@@ -9663,7 +9668,7 @@ ${section}
9663
9668
  };
9664
9669
  let filePath;
9665
9670
  try {
9666
- filePath = path7.join(boardDir, (0, taskFile_1.taskFileName)(taskId));
9671
+ filePath = path11.join(boardDir, (0, taskFile_1.taskFileName)(taskId));
9667
9672
  } catch (err) {
9668
9673
  return { success: false, error: err.message };
9669
9674
  }
@@ -9717,7 +9722,7 @@ ${section}
9717
9722
  if (!doc) {
9718
9723
  return { success: false, error: `Task not found: ${taskId}` };
9719
9724
  }
9720
- let filePath = doc.filePath ?? path7.join(dirs.boardDir, (0, taskFile_1.taskFileName)(taskId));
9725
+ let filePath = doc.filePath ?? path11.join(dirs.boardDir, (0, taskFile_1.taskFileName)(taskId));
9721
9726
  if (doc.task.column === resolvedColumn.id) {
9722
9727
  return { success: true, task: doc.task, filePath, column: resolvedColumn, noop: true };
9723
9728
  }
@@ -9933,7 +9938,7 @@ ${section}
9933
9938
  let childStates;
9934
9939
  let incompleteChildren;
9935
9940
  if (doc.task.type === "epic") {
9936
- const boardDir = path7.dirname(taskPath);
9941
+ const boardDir = path11.dirname(taskPath);
9937
9942
  childStates = resolveEpicChildStates(doc.task, boardDir, logsDir);
9938
9943
  const incomplete = childStates.filter((child) => child.status === "active").map((child) => ({ id: child.id, title: child.title }));
9939
9944
  if (incomplete.length > 0) {
@@ -9954,8 +9959,8 @@ ${section}
9954
9959
  completedAt: now,
9955
9960
  updatedAt: now
9956
9961
  };
9957
- const destPath = path7.join(logsDir, path7.basename(taskPath));
9958
- if (fs7.existsSync(destPath)) {
9962
+ const destPath = path11.join(logsDir, path11.basename(taskPath));
9963
+ if (fs11.existsSync(destPath)) {
9959
9964
  return { success: false, error: `Task already exists in logs: ${doc.task.id}` };
9960
9965
  }
9961
9966
  const completedBody = archivedBody(doc, childStates);
@@ -9972,14 +9977,14 @@ ${section}
9972
9977
  return { success: false, error: `Failed to append ledger record: ${err}` };
9973
9978
  }
9974
9979
  try {
9975
- fs7.mkdirSync(logsDir, { recursive: true });
9980
+ fs11.mkdirSync(logsDir, { recursive: true });
9976
9981
  writeTaskFileExclusive(destPath, completedTask, completedBody);
9977
9982
  } catch (err) {
9978
9983
  rollbackLedgerAppend(logsDir, record);
9979
9984
  return { success: false, error: `Failed to complete task: ${err}` };
9980
9985
  }
9981
9986
  try {
9982
- fs7.unlinkSync(taskPath);
9987
+ fs11.unlinkSync(taskPath);
9983
9988
  return {
9984
9989
  success: true,
9985
9990
  task: completedTask,
@@ -9988,7 +9993,7 @@ ${section}
9988
9993
  };
9989
9994
  } catch (err) {
9990
9995
  try {
9991
- fs7.unlinkSync(destPath);
9996
+ fs11.unlinkSync(destPath);
9992
9997
  } catch {
9993
9998
  }
9994
9999
  rollbackLedgerAppend(logsDir, record);
@@ -10001,7 +10006,7 @@ ${section}
10001
10006
  return { success: false, error: `Failed to read task file: ${taskPath}` };
10002
10007
  }
10003
10008
  try {
10004
- fs7.unlinkSync(taskPath);
10009
+ fs11.unlinkSync(taskPath);
10005
10010
  return { success: true, task: doc.task };
10006
10011
  } catch (err) {
10007
10012
  return { success: false, error: `Failed to delete task file: ${err}` };
@@ -10076,7 +10081,7 @@ ${section}
10076
10081
  }
10077
10082
  function findTask(boardDir, taskId) {
10078
10083
  try {
10079
- const directPath = path7.join(boardDir, (0, taskFile_1.taskFileName)(taskId));
10084
+ const directPath = path11.join(boardDir, (0, taskFile_1.taskFileName)(taskId));
10080
10085
  const directDoc = (0, taskFile_1.readTaskFile)(directPath);
10081
10086
  if (directDoc && directDoc.task.id === taskId) {
10082
10087
  return directDoc;
@@ -10309,7 +10314,7 @@ ${section}
10309
10314
  if (doc.task.contract?.status !== "draft")
10310
10315
  continue;
10311
10316
  const task = applyContractActivation(doc.task, (/* @__PURE__ */ new Date()).toISOString());
10312
- const taskPath = doc.filePath ?? path7.join(boardDir, (0, taskFile_1.taskFileName)(task.id));
10317
+ const taskPath = doc.filePath ?? path11.join(boardDir, (0, taskFile_1.taskFileName)(task.id));
10313
10318
  (0, taskFile_1.writeTaskFile)(taskPath, task, doc.body);
10314
10319
  activated.push(task.id);
10315
10320
  }
@@ -10669,10 +10674,10 @@ var require_dist = __commonJS({
10669
10674
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
10670
10675
  };
10671
10676
  Object.defineProperty(exports, "__esModule", { value: true });
10672
- exports.archiveTasks = exports.deleteTasks = 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.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;
10673
- exports.DOT_BRAINFILE_GITIGNORE_BASENAME = exports.BRAINFILE_STATE_BASENAME = 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 = void 0;
10674
- exports.getEffectiveState = exports.activateTaskContractsByParent = 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 = void 0;
10675
- 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 = void 0;
10677
+ 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
+ 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
+ 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;
10676
10681
  __exportStar(require_types2(), exports);
10677
10682
  __exportStar(require_contract(), exports);
10678
10683
  var contract_1 = require_contract2();
@@ -10741,6 +10746,13 @@ var require_dist = __commonJS({
10741
10746
  Object.defineProperty(exports, "BrainfileSerializer", { enumerable: true, get: function() {
10742
10747
  return serializer_1.BrainfileSerializer;
10743
10748
  } });
10749
+ var frontmatter_1 = require_frontmatter();
10750
+ Object.defineProperty(exports, "parseFrontmatter", { enumerable: true, get: function() {
10751
+ return frontmatter_1.parseFrontmatter;
10752
+ } });
10753
+ Object.defineProperty(exports, "serializeFrontmatter", { enumerable: true, get: function() {
10754
+ return frontmatter_1.serializeFrontmatter;
10755
+ } });
10744
10756
  var validator_1 = require_validator();
10745
10757
  Object.defineProperty(exports, "BrainfileValidator", { enumerable: true, get: function() {
10746
10758
  return validator_1.BrainfileValidator;
@@ -11508,8 +11520,8 @@ function renderCliError(error) {
11508
11520
 
11509
11521
  // src/utils/brainfile-path.ts
11510
11522
  var import_core3 = __toESM(require_dist());
11511
- import * as fs2 from "fs";
11512
- import * as path2 from "path";
11523
+ import * as fs3 from "fs";
11524
+ import * as path3 from "path";
11513
11525
 
11514
11526
  // src/utils/v2-only.ts
11515
11527
  var import_core2 = __toESM(require_dist());
@@ -11530,78 +11542,473 @@ function assertV2Brainfile(brainfilePath) {
11530
11542
  }
11531
11543
  }
11532
11544
 
11545
+ // src/utils/board-repo.ts
11546
+ import * as fs2 from "fs";
11547
+ import * as os from "os";
11548
+ import * as path2 from "path";
11549
+ import { spawnSync } from "child_process";
11550
+ var DEFAULT_BOARD_BRANCH = "brainfile";
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
+ }
11567
+ var EXCLUDE_ENTRY = ".brainfile/";
11568
+ var EXCLUDE_MARKER = "# brainfile: board worktree (managed by `brainfile`)";
11569
+ var FALLBACK_IDENTITY_NAME = "brainfile";
11570
+ var FALLBACK_IDENTITY_EMAIL = "brainfile@localhost";
11571
+ var MAX_MESSAGE_LENGTH = 72;
11572
+ var BOARD_SUBDIRS = ["board", "logs"];
11573
+ function git(args, cwd, env, timeoutMs) {
11574
+ const result = spawnSync("git", args, {
11575
+ cwd,
11576
+ encoding: "utf-8",
11577
+ env: env ? { ...process.env, ...env } : process.env,
11578
+ stdio: ["ignore", "pipe", "pipe"],
11579
+ ...timeoutMs ? { timeout: timeoutMs, killSignal: "SIGKILL" } : {}
11580
+ });
11581
+ return {
11582
+ ok: result.status === 0,
11583
+ status: result.status,
11584
+ stdout: (result.stdout ?? "").toString().trim(),
11585
+ stderr: (result.stderr ?? "").toString().trim()
11586
+ };
11587
+ }
11588
+ function gitToplevel(cwd) {
11589
+ const r = git(["rev-parse", "--show-toplevel"], cwd);
11590
+ return r.ok && r.stdout ? path2.resolve(r.stdout) : null;
11591
+ }
11592
+ function gitCommonDir(cwd) {
11593
+ const r = git(["rev-parse", "--git-common-dir"], cwd);
11594
+ if (!r.ok || !r.stdout) return null;
11595
+ return path2.resolve(cwd, r.stdout);
11596
+ }
11597
+ function listWorktrees(cwd) {
11598
+ const r = git(["worktree", "list", "--porcelain"], cwd);
11599
+ if (!r.ok) return [];
11600
+ const entries = [];
11601
+ let current = null;
11602
+ for (const line of r.stdout.split("\n")) {
11603
+ if (line.startsWith("worktree ")) {
11604
+ current = { path: path2.resolve(line.slice("worktree ".length)), bare: false, detached: false };
11605
+ entries.push(current);
11606
+ } else if (!current) {
11607
+ continue;
11608
+ } else if (line.startsWith("HEAD ")) {
11609
+ current.head = line.slice(5);
11610
+ } else if (line.startsWith("branch ")) {
11611
+ current.branch = line.slice(7);
11612
+ } else if (line === "bare") {
11613
+ current.bare = true;
11614
+ } else if (line === "detached") {
11615
+ current.detached = true;
11616
+ }
11617
+ }
11618
+ return entries;
11619
+ }
11620
+ function boardBranch(cwd) {
11621
+ const r = git(["config", "--get", BOARD_BRANCH_CONFIG_KEY], cwd);
11622
+ return r.ok && r.stdout ? r.stdout : DEFAULT_BOARD_BRANCH;
11623
+ }
11624
+ function boardRepoKind(dotDir) {
11625
+ try {
11626
+ const st = fs2.statSync(path2.join(dotDir, ".git"));
11627
+ if (st.isFile()) return "linked";
11628
+ if (st.isDirectory()) return "standalone";
11629
+ } catch {
11630
+ }
11631
+ return null;
11632
+ }
11633
+ function isTrackedBoard(dotDir) {
11634
+ return boardRepoKind(dotDir) !== null;
11635
+ }
11636
+ function findTrackedBoardDir(cwd) {
11637
+ const worktrees = listWorktrees(cwd);
11638
+ if (worktrees.length === 0) return null;
11639
+ const wanted = `refs/heads/${boardBranch(cwd)}`;
11640
+ const hit = worktrees.find((w) => w.branch === wanted);
11641
+ if (!hit) return null;
11642
+ return fs2.existsSync(path2.join(hit.path, "brainfile.md")) ? hit.path : null;
11643
+ }
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);
11679
+ }
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);
11713
+ const worktrees = listWorktrees(cwd);
11714
+ if (worktrees.length === 0) return null;
11715
+ if (worktrees.some((w) => w.branch === `refs/heads/${branch}`)) return null;
11716
+ const main = worktrees[0];
11717
+ let target;
11718
+ if (main.bare) {
11719
+ const common = gitCommonDir(cwd);
11720
+ if (!common) return null;
11721
+ target = path2.join(common, "brainfile");
11722
+ } else {
11723
+ target = path2.join(main.path, ".brainfile");
11724
+ }
11725
+ if (fs2.existsSync(target)) return null;
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);
11729
+ if (!r.ok) return null;
11730
+ ensureExcludeEntry(cwd);
11731
+ for (const sub of BOARD_SUBDIRS) fs2.mkdirSync(path2.join(target, sub), { recursive: true });
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
+ }
11738
+ const relative3 = path2.relative(process.cwd(), target) || ".";
11739
+ process.stderr.write(
11740
+ start.remote ? `Checked out the shared board from ${start.remote} into ${relative3}/
11741
+ ` : `Checked out the board from the '${branch}' branch into ${relative3}/
11742
+ `
11743
+ );
11744
+ return target;
11745
+ }
11746
+ function ensureExcludeEntry(cwd) {
11747
+ const common = gitCommonDir(cwd);
11748
+ if (!common) return false;
11749
+ const file = path2.join(common, "info", "exclude");
11750
+ const existing = fs2.existsSync(file) ? fs2.readFileSync(file, "utf-8") : "";
11751
+ const present = existing.split(/\r?\n/).some((line) => line.trim() === EXCLUDE_ENTRY || line.trim() === ".brainfile");
11752
+ if (present) return false;
11753
+ fs2.mkdirSync(path2.dirname(file), { recursive: true });
11754
+ const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
11755
+ fs2.appendFileSync(file, `${separator}${EXCLUDE_MARKER}
11756
+ ${EXCLUDE_ENTRY}
11757
+ `, "utf-8");
11758
+ return true;
11759
+ }
11760
+ function removeExcludeEntry(cwd) {
11761
+ const common = gitCommonDir(cwd);
11762
+ if (!common) return false;
11763
+ const file = path2.join(common, "info", "exclude");
11764
+ if (!fs2.existsSync(file)) return false;
11765
+ const lines = fs2.readFileSync(file, "utf-8").split(/\r?\n/);
11766
+ const at = lines.indexOf(EXCLUDE_MARKER);
11767
+ if (at < 0) return false;
11768
+ const removeCount = lines[at + 1]?.trim() === EXCLUDE_ENTRY ? 2 : 1;
11769
+ lines.splice(at, removeCount);
11770
+ fs2.writeFileSync(file, lines.join("\n"), "utf-8");
11771
+ return true;
11772
+ }
11773
+ function createOrphanBoardWorktree(repoCwd, dotDir, branch) {
11774
+ const r = git(["worktree", "add", "--quiet", "--orphan", "-b", branch, dotDir], repoCwd);
11775
+ if (!r.ok) {
11776
+ throw new Error(
11777
+ `Could not create the board worktree on branch '${branch}' (git >= 2.42 is required for --orphan): ${r.stderr}`
11778
+ );
11779
+ }
11780
+ }
11781
+ function initStandaloneBoardRepo(dotDir) {
11782
+ const parent = path2.dirname(dotDir);
11783
+ let r = git(["init", "--quiet", "-b", "main", dotDir], parent);
11784
+ if (!r.ok) r = git(["init", "--quiet", dotDir], parent);
11785
+ if (!r.ok) throw new Error(`Could not initialize a git repository in ${dotDir}: ${r.stderr}`);
11786
+ }
11787
+ function boardIsDirty(dotDir) {
11788
+ const r = git(["status", "--porcelain"], dotDir);
11789
+ return r.ok && r.stdout.length > 0;
11790
+ }
11791
+ function unmergedFiles(dotDir) {
11792
+ const r = git(["diff", "--name-only", "--diff-filter=U"], dotDir);
11793
+ return r.ok ? r.stdout.split("\n").map((l) => l.trim()).filter(Boolean) : [];
11794
+ }
11795
+ function boardDirtyFiles(dotDir) {
11796
+ const r = git(["status", "--porcelain"], dotDir);
11797
+ if (!r.ok || !r.stdout) return [];
11798
+ return r.stdout.split("\n").map((line) => line.replace(/^[ MADRCU?!]{1,2}\s+/, "").replace(/^.* -> /, "").trim()).filter(Boolean);
11799
+ }
11800
+ function identityArgs(dotDir) {
11801
+ const args = [];
11802
+ if (!git(["config", "--get", "user.name"], dotDir).stdout) args.push("-c", `user.name=${FALLBACK_IDENTITY_NAME}`);
11803
+ if (!git(["config", "--get", "user.email"], dotDir).stdout) args.push("-c", `user.email=${FALLBACK_IDENTITY_EMAIL}`);
11804
+ return args;
11805
+ }
11806
+ var commitListeners = [];
11807
+ function onBoardCommitted(listener) {
11808
+ if (!commitListeners.includes(listener)) commitListeners.push(listener);
11809
+ }
11810
+ function notifyCommitted(dotDir) {
11811
+ for (const listener of commitListeners) {
11812
+ try {
11813
+ listener(dotDir);
11814
+ } catch {
11815
+ }
11816
+ }
11817
+ }
11818
+ function ensureBoardDirs(dotDir) {
11819
+ let created = false;
11820
+ for (const sub of BOARD_SUBDIRS) {
11821
+ const dir = path2.join(dotDir, sub);
11822
+ try {
11823
+ fs2.mkdirSync(dir, { recursive: true });
11824
+ const keep = path2.join(dir, ".gitkeep");
11825
+ if (!fs2.existsSync(keep)) {
11826
+ fs2.writeFileSync(keep, "", "utf-8");
11827
+ created = true;
11828
+ }
11829
+ } catch {
11830
+ }
11831
+ }
11832
+ return created;
11833
+ }
11834
+ function commitBoard(dotDir, options) {
11835
+ if (!isTrackedBoard(dotDir)) return false;
11836
+ ensureBoardDirs(dotDir);
11837
+ if (unmergedFiles(dotDir).length > 0) return false;
11838
+ if (!git(["add", "-A"], dotDir).ok) return false;
11839
+ if (!boardIsDirty(dotDir)) return false;
11840
+ const agent = options.agent?.replace(/[[\]<>\r\n]/g, "").trim();
11841
+ const suffix = agent ? ` [${agent}]` : "";
11842
+ const message = oneLine(options.message, suffix);
11843
+ const r = git(
11844
+ [...identityArgs(dotDir), "-c", "commit.gpgsign=false", "commit", "--quiet", "--no-verify", "-m", message],
11845
+ dotDir
11846
+ );
11847
+ if (r.ok) notifyCommitted(dotDir);
11848
+ return r.ok;
11849
+ }
11850
+ function commitHandEdits(dotDir) {
11851
+ if (!isTrackedBoard(dotDir)) return false;
11852
+ const keepOnly = (f) => f.endsWith(".gitkeep") || BOARD_SUBDIRS.some((sub) => f === `${sub}/`);
11853
+ if (ensureBoardDirs(dotDir) && boardDirtyFiles(dotDir).every(keepOnly)) {
11854
+ commitBoard(dotDir, { message: "chore: keep board/ and logs/ in git" });
11855
+ }
11856
+ const files = boardDirtyFiles(dotDir);
11857
+ if (files.length === 0) return false;
11858
+ const summary = files.length <= 3 ? files.join(", ") : `${files.length} files`;
11859
+ return commitBoard(dotDir, { message: `edit: ${summary}` });
11860
+ }
11861
+ function homeBoardDir() {
11862
+ return path2.join(os.homedir(), ".brainfile");
11863
+ }
11864
+ function isGlobalBoardRequested(argv = process.argv) {
11865
+ if (process.env.BRAINFILE_GLOBAL === "1") return true;
11866
+ return argv.includes("-g") || argv.includes("--global");
11867
+ }
11868
+ function oneLine(message, suffix = "") {
11869
+ const flat = message.replace(/\s+/g, " ").trim();
11870
+ const room = Math.max(1, MAX_MESSAGE_LENGTH - suffix.length);
11871
+ return `${flat.length > room ? `${flat.slice(0, room - 1)}\u2026` : flat}${suffix}`;
11872
+ }
11873
+ function describeCommandForCommit(command) {
11874
+ const names = [];
11875
+ let cursor = command;
11876
+ while (cursor && cursor.parent) {
11877
+ names.unshift(cursor.name());
11878
+ cursor = cursor.parent;
11879
+ }
11880
+ const opts = command.opts();
11881
+ const idValue = opts.task ?? opts.taskId;
11882
+ const id = Array.isArray(idValue) ? idValue.join(",") : typeof idValue === "string" ? idValue : "";
11883
+ const positional = command.args.filter((a) => typeof a === "string" && a.length > 0);
11884
+ const detail = [opts.title, opts.column, positional.join(" ")].find((v) => typeof v === "string" && v.length > 0) ?? "";
11885
+ const head = `${names.join(" ")}${id ? ` ${id}` : ""}`;
11886
+ return oneLine(detail ? `${head}: ${detail}` : head);
11887
+ }
11888
+ var pendingCommits = /* @__PURE__ */ new Map();
11889
+ var flushTimer = null;
11890
+ var exitHookInstalled = false;
11891
+ function scheduleBoardCommit(brainfilePath, message, delayMs = 2e3) {
11892
+ const dotDir = path2.dirname(path2.resolve(brainfilePath));
11893
+ if (!isTrackedBoard(dotDir)) return;
11894
+ const entry = pendingCommits.get(dotDir) ?? { messages: [] };
11895
+ entry.messages.push(message);
11896
+ pendingCommits.set(dotDir, entry);
11897
+ if (flushTimer) clearTimeout(flushTimer);
11898
+ flushTimer = setTimeout(flushBoardCommits, delayMs);
11899
+ flushTimer.unref?.();
11900
+ if (!exitHookInstalled) {
11901
+ exitHookInstalled = true;
11902
+ process.once("exit", flushBoardCommits);
11903
+ }
11904
+ }
11905
+ function flushBoardCommits() {
11906
+ if (flushTimer) {
11907
+ clearTimeout(flushTimer);
11908
+ flushTimer = null;
11909
+ }
11910
+ for (const [dotDir, entry] of pendingCommits) {
11911
+ const [first, ...rest] = entry.messages;
11912
+ const message = rest.length === 0 ? first : `${first} (+${rest.length} more)`;
11913
+ commitBoard(dotDir, { message });
11914
+ }
11915
+ pendingCommits.clear();
11916
+ }
11917
+
11533
11918
  // src/utils/brainfile-path.ts
11534
11919
  function isMigrationCommand() {
11535
11920
  return process.argv.includes("migrate");
11536
11921
  }
11537
11922
  function migrationHintForPath2(brainfilePath) {
11538
- const absolute = path2.resolve(brainfilePath);
11539
- const dir = path2.basename(path2.dirname(absolute)) === ".brainfile" ? path2.dirname(path2.dirname(absolute)) : path2.dirname(absolute);
11923
+ const absolute = path3.resolve(brainfilePath);
11924
+ const dir = path3.basename(path3.dirname(absolute)) === ".brainfile" ? path3.dirname(path3.dirname(absolute)) : path3.dirname(absolute);
11540
11925
  return `Run: brainfile migrate --dir ${dir}`;
11541
11926
  }
11542
11927
  function rejectLegacyRuntimePath(resolvedPath) {
11543
- if (!isMigrationCommand() && fs2.existsSync(resolvedPath) && !(0, import_core3.isV2)(resolvedPath)) {
11928
+ if (!isMigrationCommand() && fs3.existsSync(resolvedPath) && !(0, import_core3.isV2)(resolvedPath)) {
11544
11929
  throw new CLIError(V1_UNSUPPORTED_MESSAGE, void 0, migrationHintForPath2(resolvedPath));
11545
11930
  }
11546
11931
  return resolvedPath;
11547
11932
  }
11933
+ function isPlaceholder(filePath) {
11934
+ return filePath === void 0 || filePath === import_core3.BRAINFILE_BASENAME || filePath === `./${import_core3.BRAINFILE_BASENAME}`;
11935
+ }
11548
11936
  function resolveCliBrainfilePath(filePath) {
11937
+ if (isPlaceholder(filePath) && isGlobalBoardRequested()) {
11938
+ return rejectLegacyRuntimePath(path3.join(homeBoardDir(), import_core3.BRAINFILE_BASENAME));
11939
+ }
11549
11940
  if (filePath) {
11550
- const resolved = path2.resolve(process.cwd(), filePath);
11941
+ const resolved = path3.resolve(process.cwd(), filePath);
11551
11942
  try {
11552
- if (fs2.statSync(resolved).isDirectory()) {
11943
+ if (fs3.statSync(resolved).isDirectory()) {
11553
11944
  const found = (0, import_core3.findBrainfile)(resolved);
11554
11945
  if (found && found.projectRoot === resolved) {
11555
11946
  return rejectLegacyRuntimePath(found.absolutePath);
11556
11947
  }
11557
- const dotDir = path2.join(resolved, ".brainfile", "brainfile.md");
11948
+ const dotDir = path3.join(resolved, ".brainfile", "brainfile.md");
11558
11949
  return rejectLegacyRuntimePath(dotDir);
11559
11950
  }
11560
11951
  } catch {
11561
11952
  }
11562
11953
  }
11563
- return rejectLegacyRuntimePath((0, import_core3.resolveBrainfilePath)({ filePath, startDir: process.cwd() }));
11954
+ const cwd = process.cwd();
11955
+ if (isPlaceholder(filePath)) {
11956
+ const found = (0, import_core3.findBrainfile)(cwd, { stopAtGitRoot: true });
11957
+ if (found) return rejectLegacyRuntimePath(found.absolutePath);
11958
+ const tracked = findTrackedBoardDir(cwd) ?? materializeBoardWorktree(cwd);
11959
+ if (tracked) return rejectLegacyRuntimePath(path3.join(tracked, import_core3.BRAINFILE_BASENAME));
11960
+ }
11961
+ return rejectLegacyRuntimePath((0, import_core3.resolveBrainfilePath)({ filePath, startDir: cwd, stopAtGitRoot: true }));
11962
+ }
11963
+ function tryResolveBoardDir(filePath) {
11964
+ try {
11965
+ const resolved = resolveCliBrainfilePath(filePath);
11966
+ if (!fs3.existsSync(resolved)) return null;
11967
+ return path3.dirname(resolved);
11968
+ } catch {
11969
+ return null;
11970
+ }
11564
11971
  }
11565
11972
 
11566
11973
  // src/utils/v2-detect.ts
11567
11974
  var import_core4 = __toESM(require_dist());
11568
11975
  var import_core5 = __toESM(require_dist());
11569
11976
  var import_core6 = __toESM(require_dist());
11570
- import * as fs4 from "fs";
11571
- import * as path4 from "path";
11977
+ import * as fs5 from "fs";
11978
+ import * as path5 from "path";
11572
11979
 
11573
11980
  // src/utils/workspace-format.ts
11574
- import * as fs3 from "fs";
11575
- import * as path3 from "path";
11981
+ import * as fs4 from "fs";
11982
+ import * as path4 from "path";
11576
11983
  function existsFile(filePath) {
11577
11984
  try {
11578
- return fs3.statSync(filePath).isFile();
11985
+ return fs4.statSync(filePath).isFile();
11579
11986
  } catch {
11580
11987
  return false;
11581
11988
  }
11582
11989
  }
11583
11990
  function existsDir(dirPath) {
11584
11991
  try {
11585
- return fs3.statSync(dirPath).isDirectory();
11992
+ return fs4.statSync(dirPath).isDirectory();
11586
11993
  } catch {
11587
11994
  return false;
11588
11995
  }
11589
11996
  }
11590
11997
  function workspaceRootFromBrainfilePath(brainfilePath) {
11591
- const absolute = path3.resolve(brainfilePath);
11592
- const dir = path3.dirname(absolute);
11593
- return path3.basename(dir) === ".brainfile" ? path3.dirname(dir) : dir;
11998
+ const absolute = path4.resolve(brainfilePath);
11999
+ const dir = path4.dirname(absolute);
12000
+ return path4.basename(dir) === ".brainfile" ? path4.dirname(dir) : dir;
11594
12001
  }
11595
12002
  function probeWorkspaceFormat(rootDir = process.cwd()) {
11596
- const resolvedRoot = path3.resolve(rootDir);
11597
- const dotDir = path3.join(resolvedRoot, ".brainfile");
12003
+ const resolvedRoot = path4.resolve(rootDir);
12004
+ const dotDir = path4.join(resolvedRoot, ".brainfile");
11598
12005
  const paths = {
11599
12006
  rootDir: resolvedRoot,
11600
- rootBrainfilePath: path3.join(resolvedRoot, "brainfile.md"),
12007
+ rootBrainfilePath: path4.join(resolvedRoot, "brainfile.md"),
11601
12008
  dotDir,
11602
- dotBrainfilePath: path3.join(dotDir, "brainfile.md"),
11603
- boardDir: path3.join(dotDir, "board"),
11604
- logsDir: path3.join(dotDir, "logs")
12009
+ dotBrainfilePath: path4.join(dotDir, "brainfile.md"),
12010
+ boardDir: path4.join(dotDir, "board"),
12011
+ logsDir: path4.join(dotDir, "logs")
11605
12012
  };
11606
12013
  const presence = {
11607
12014
  rootBrainfile: existsFile(paths.rootBrainfilePath),
@@ -11635,15 +12042,15 @@ function shouldSuggestMigration(probe) {
11635
12042
  // src/utils/v2-detect.ts
11636
12043
  var v2MigrationHintShown = /* @__PURE__ */ new Set();
11637
12044
  function shouldSuggestV2Migration(brainfilePath) {
11638
- const resolved = path4.resolve(brainfilePath);
12045
+ const resolved = path5.resolve(brainfilePath);
11639
12046
  if (v2MigrationHintShown.has(resolved)) return false;
11640
12047
  const probe = probeWorkspaceForBrainfile(resolved);
11641
12048
  if (shouldSuggestMigration(probe)) {
11642
12049
  return true;
11643
12050
  }
11644
- if (!(0, import_core6.isV2)(resolved) && fs4.existsSync(resolved)) {
12051
+ if (!(0, import_core6.isV2)(resolved) && fs5.existsSync(resolved)) {
11645
12052
  try {
11646
- const content = fs4.readFileSync(resolved, "utf-8");
12053
+ const content = fs5.readFileSync(resolved, "utf-8");
11647
12054
  const parsed = import_core4.Brainfile.parseWithErrors(content);
11648
12055
  if (parsed.board) return true;
11649
12056
  } catch {
@@ -11652,42 +12059,625 @@ function shouldSuggestV2Migration(brainfilePath) {
11652
12059
  return false;
11653
12060
  }
11654
12061
  function markV2MigrationHintShown(brainfilePath) {
11655
- v2MigrationHintShown.add(path4.resolve(brainfilePath));
12062
+ v2MigrationHintShown.add(path5.resolve(brainfilePath));
12063
+ }
12064
+
12065
+ // src/commands/merge-driver.ts
12066
+ var import_core7 = __toESM(require_dist());
12067
+ import * as fs6 from "fs";
12068
+ import * as os2 from "os";
12069
+ import * as path6 from "path";
12070
+ import { spawnSync as spawnSync2 } from "child_process";
12071
+ function parseDoc(content) {
12072
+ const parsed = (0, import_core7.parseFrontmatter)(content);
12073
+ if (!parsed) return null;
12074
+ return { data: parsed.data, body: parsed.body };
12075
+ }
12076
+ function same(a, b) {
12077
+ return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
12078
+ }
12079
+ function stamp(data) {
12080
+ const value = data.updatedAt;
12081
+ return typeof value === "string" ? value : "";
12082
+ }
12083
+ function mergeFields(base, ours, theirs) {
12084
+ const keys = [];
12085
+ for (const key of [...Object.keys(ours), ...Object.keys(theirs), ...Object.keys(base)]) {
12086
+ if (!keys.includes(key)) keys.push(key);
12087
+ }
12088
+ const oursWins = stamp(ours) > stamp(theirs);
12089
+ const out = {};
12090
+ for (const key of keys) {
12091
+ const b = base[key];
12092
+ const o = ours[key];
12093
+ const t = theirs[key];
12094
+ let pick;
12095
+ if (same(o, t)) pick = o;
12096
+ else if (same(b, o)) pick = t;
12097
+ else if (same(b, t)) pick = o;
12098
+ else if (key === "updatedAt") pick = String(o ?? "") > String(t ?? "") ? o : t;
12099
+ else pick = oursWins ? o : t;
12100
+ if (pick !== void 0) out[key] = pick;
12101
+ }
12102
+ return out;
12103
+ }
12104
+ var CONFLICT = /^<{7}[^\n]*\n([\s\S]*?)^={7}\n([\s\S]*?)^>{7}[^\n]*\n?/gm;
12105
+ var LIST_ITEM = /^\s*[-*]\s+/;
12106
+ var LEADING_STAMP = /^\s*[-*]\s+(\d{4}-\d{2}-\d{2}T[0-9:.]+Z?)\b/;
12107
+ function unionLists(ours, theirs) {
12108
+ const a = ours.split("\n").filter((l) => l.trim().length > 0);
12109
+ const b = theirs.split("\n").filter((l) => l.trim().length > 0);
12110
+ if (![...a, ...b].every((l) => LIST_ITEM.test(l))) return null;
12111
+ const merged = [...a];
12112
+ for (const line of b) if (!merged.includes(line)) merged.push(line);
12113
+ if (merged.every((l) => LEADING_STAMP.test(l))) {
12114
+ merged.sort((x, y) => {
12115
+ const sx = LEADING_STAMP.exec(x)?.[1] ?? "";
12116
+ const sy = LEADING_STAMP.exec(y)?.[1] ?? "";
12117
+ return sx < sy ? -1 : sx > sy ? 1 : 0;
12118
+ });
12119
+ }
12120
+ return `${merged.join("\n")}
12121
+ `;
12122
+ }
12123
+ function mergeBodies(base, ours, theirs) {
12124
+ if (ours === theirs) return { content: ours, clean: true };
12125
+ if (base === ours) return { content: theirs, clean: true };
12126
+ if (base === theirs) return { content: ours, clean: true };
12127
+ const dir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "brainfile-merge-"));
12128
+ try {
12129
+ const files = { base: path6.join(dir, "base"), ours: path6.join(dir, "ours"), theirs: path6.join(dir, "theirs") };
12130
+ fs6.writeFileSync(files.base, base);
12131
+ fs6.writeFileSync(files.ours, ours);
12132
+ fs6.writeFileSync(files.theirs, theirs);
12133
+ const r = spawnSync2("git", ["merge-file", "-p", "-L", "ours", "-L", "base", "-L", "theirs", files.ours, files.base, files.theirs], {
12134
+ encoding: "utf-8",
12135
+ stdio: ["ignore", "pipe", "pipe"]
12136
+ });
12137
+ if (r.status === null || r.status < 0) {
12138
+ return { content: `<<<<<<< ours
12139
+ ${ours}=======
12140
+ ${theirs}>>>>>>> theirs
12141
+ `, clean: false };
12142
+ }
12143
+ if (r.status === 0) return { content: r.stdout, clean: true };
12144
+ let clean = true;
12145
+ const content = r.stdout.replace(CONFLICT, (whole, oursHunk, theirsHunk) => {
12146
+ const union = unionLists(oursHunk, theirsHunk);
12147
+ if (union === null) {
12148
+ clean = false;
12149
+ return whole;
12150
+ }
12151
+ return union;
12152
+ });
12153
+ return { content, clean };
12154
+ } finally {
12155
+ fs6.rmSync(dir, { recursive: true, force: true });
12156
+ }
12157
+ }
12158
+ function mergeBoardDocuments(base, ours, theirs) {
12159
+ const b = parseDoc(base);
12160
+ const o = parseDoc(ours);
12161
+ const t = parseDoc(theirs);
12162
+ if (!o || !t) return mergeBodies(base, ours, theirs);
12163
+ const data = mergeFields(b?.data ?? {}, o.data, t.data);
12164
+ const body = mergeBodies(b?.body ?? "", o.body, t.body);
12165
+ return { content: (0, import_core7.serializeFrontmatter)(data, body.content), clean: body.clean };
12166
+ }
12167
+ function summarizeFieldChanges(before, after) {
12168
+ const b = parseDoc(before)?.data ?? {};
12169
+ const a = parseDoc(after)?.data ?? {};
12170
+ const parts = [];
12171
+ for (const key of Object.keys(a)) {
12172
+ if (key === "updatedAt" || same(b[key], a[key])) continue;
12173
+ const render = (v) => typeof v === "string" ? v : JSON.stringify(v);
12174
+ parts.push(`${key}: ${b[key] === void 0 ? "\u2205" : render(b[key])} \u2192 ${render(a[key])}`);
12175
+ }
12176
+ return parts.join("; ");
12177
+ }
12178
+ function mergeDriverCommand(options) {
12179
+ const read = (p) => fs6.existsSync(p) ? fs6.readFileSync(p, "utf-8") : "";
12180
+ if (!options.ours || !options.theirs) return 2;
12181
+ const outcome = mergeBoardDocuments(read(options.base), read(options.ours), read(options.theirs));
12182
+ fs6.writeFileSync(options.ours, outcome.content, "utf-8");
12183
+ return outcome.clean ? 0 : 1;
12184
+ }
12185
+
12186
+ // src/utils/board-sync.ts
12187
+ import * as fs7 from "fs";
12188
+ import * as os3 from "os";
12189
+ import * as path7 from "path";
12190
+ var REMOTE_CONFIG_KEY = "brainfile.remote";
12191
+ var LEGACY_CHECKED_CONFIG_KEY = "brainfile.legacyBranchChecked";
12192
+ var AUTOSYNC_CONFIG_KEY = "brainfile.autosync";
12193
+ var URL_REMOTE_NAME = "board";
12194
+ var MERGE_DRIVER_NAME = "brainfile";
12195
+ var DEFAULT_MERGE_DRIVER_COMMAND = "brainfile merge-driver %O %A %B";
12196
+ var SYNC_STATE_FILE = path7.join("state", "sync.json");
12197
+ var BOARD_GITATTRIBUTES = [
12198
+ "logs/ledger.jsonl merge=union",
12199
+ "board/*.md merge=brainfile",
12200
+ "logs/*.md merge=brainfile",
12201
+ "brainfile.md merge=brainfile"
12202
+ ];
12203
+ function configGet(dotDir, key) {
12204
+ const r = git(["config", "--get", key], dotDir);
12205
+ return r.ok && r.stdout ? r.stdout : null;
12206
+ }
12207
+ function configSet(dotDir, key, value) {
12208
+ return git(["config", key, value], dotDir).ok;
12209
+ }
12210
+ function boardRemote(dotDir) {
12211
+ return configGet(dotDir, REMOTE_CONFIG_KEY);
12212
+ }
12213
+ function boardRemoteName(dotDir) {
12214
+ const configured = configGet(dotDir, BOARD_NAME_CONFIG_KEY) ?? configGet(dotDir, LEGACY_REMOTE_BRANCH_CONFIG_KEY);
12215
+ if (configured) return configured;
12216
+ return defaultName(dotDir, DEFAULT_BOARD_NAME);
12217
+ }
12218
+ function defaultName(dotDir, inRepo) {
12219
+ if (path7.resolve(dotDir) === path7.resolve(homeBoardDir())) return "home";
12220
+ if (boardRepoKind(dotDir) === "standalone" && path7.basename(path7.resolve(dotDir)) === ".brainfile") {
12221
+ return path7.basename(path7.dirname(path7.resolve(dotDir))) || inRepo;
12222
+ }
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));
12230
+ }
12231
+ function boardAutosync(dotDir) {
12232
+ const value = configGet(dotDir, AUTOSYNC_CONFIG_KEY);
12233
+ if (value === "off" || value === "push" || value === "full") return value;
12234
+ return boardRemote(dotDir) ? "push" : "off";
12235
+ }
12236
+ function setBoardAutosync(dotDir, mode) {
12237
+ return configSet(dotDir, AUTOSYNC_CONFIG_KEY, mode);
12238
+ }
12239
+ function looksLikeRemoteUrl(target) {
12240
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(target)) return true;
12241
+ if (/^[^/\s]+@[^/\s]+:/.test(target)) return true;
12242
+ if (target.startsWith("/") || target.startsWith(".") || target.startsWith("~")) return true;
12243
+ if (target.includes("/") && fs7.existsSync(target)) return true;
12244
+ return false;
12245
+ }
12246
+ function setBoardRemote(cwd, target, boardName) {
12247
+ const trimmed = target.trim();
12248
+ if (!trimmed) throw new Error("remote is required");
12249
+ let remote;
12250
+ if (looksLikeRemoteUrl(trimmed)) {
12251
+ remote = URL_REMOTE_NAME;
12252
+ const url = trimmed.startsWith("~/") ? path7.join(os3.homedir(), trimmed.slice(2)) : trimmed;
12253
+ const exists = git(["remote", "get-url", remote], cwd).ok;
12254
+ const r = exists ? git(["remote", "set-url", remote, url], cwd) : git(["remote", "add", remote, url], cwd);
12255
+ if (!r.ok) throw new Error(r.stderr || `could not register remote '${remote}'`);
12256
+ } else {
12257
+ remote = trimmed;
12258
+ if (!git(["remote", "get-url", remote], cwd).ok) {
12259
+ const known = git(["remote"], cwd).stdout.split("\n").filter(Boolean);
12260
+ const hint = known.length ? ` Known remotes: ${known.join(", ")}.` : "";
12261
+ throw new Error(`'${remote}' is not a git remote here.${hint} Pass a URL to add one.`);
12262
+ }
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
+ }
12267
+ configSet(cwd, REMOTE_CONFIG_KEY, remote);
12268
+ if (boardName) configSet(cwd, BOARD_NAME_CONFIG_KEY, boardName);
12269
+ registerMergeDriver(cwd);
12270
+ return {
12271
+ remote,
12272
+ url: git(["remote", "get-url", remote], cwd).stdout || null,
12273
+ remoteRef: boardRemoteRef(cwd)
12274
+ };
12275
+ }
12276
+ function registerMergeDriver(cwd, command) {
12277
+ const current = configGet(cwd, `merge.${MERGE_DRIVER_NAME}.driver`);
12278
+ if (command === void 0) {
12279
+ if (current) return false;
12280
+ command = DEFAULT_MERGE_DRIVER_COMMAND;
12281
+ }
12282
+ if (current === command) return false;
12283
+ configSet(cwd, `merge.${MERGE_DRIVER_NAME}.name`, "brainfile board merge (field-level, last writer wins)");
12284
+ return configSet(cwd, `merge.${MERGE_DRIVER_NAME}.driver`, command);
12285
+ }
12286
+ function ensureBoardAttributes(dotDir) {
12287
+ const file = path7.join(dotDir, ".gitattributes");
12288
+ let existing = "";
12289
+ try {
12290
+ existing = fs7.readFileSync(file, "utf-8");
12291
+ } catch {
12292
+ }
12293
+ const lines = existing.split("\n").map((l) => l.trim());
12294
+ const missing = BOARD_GITATTRIBUTES.filter((l) => !lines.includes(l));
12295
+ if (missing.length === 0) return false;
12296
+ const prefix = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
12297
+ ` : existing;
12298
+ fs7.writeFileSync(file, `${prefix}${missing.join("\n")}
12299
+ `, "utf-8");
12300
+ return true;
12301
+ }
12302
+ function readSyncState(dotDir) {
12303
+ try {
12304
+ const parsed = JSON.parse(fs7.readFileSync(path7.join(dotDir, SYNC_STATE_FILE), "utf-8"));
12305
+ return typeof parsed?.at === "string" ? parsed : null;
12306
+ } catch {
12307
+ return null;
12308
+ }
12309
+ }
12310
+ function writeSyncState(dotDir, state) {
12311
+ try {
12312
+ fs7.mkdirSync(path7.join(dotDir, "state"), { recursive: true });
12313
+ fs7.writeFileSync(path7.join(dotDir, SYNC_STATE_FILE), `${JSON.stringify(state, null, 2)}
12314
+ `, "utf-8");
12315
+ } catch {
12316
+ }
12317
+ }
12318
+ function firstLine(text) {
12319
+ return text.split("\n").map((l) => l.trim()).find(Boolean) ?? "";
12320
+ }
12321
+ function isMissingRemoteRef(stderr) {
12322
+ return /couldn't find remote ref|Remote branch .* not found|invalid refspec|no such ref/i.test(stderr);
12323
+ }
12324
+ function commitCount(dotDir, range) {
12325
+ const r = git(["rev-list", "--count", range], dotDir);
12326
+ return r.ok ? Number.parseInt(r.stdout, 10) || 0 : 0;
12327
+ }
12328
+ function identityArgs2(dotDir) {
12329
+ const args = ["-c", "commit.gpgsign=false"];
12330
+ if (!git(["config", "--get", "user.email"], dotDir).ok) {
12331
+ args.push("-c", "user.name=brainfile", "-c", "user.email=brainfile@localhost");
12332
+ }
12333
+ return args;
12334
+ }
12335
+ function resolveCompleteVersusEdit(dotDir) {
12336
+ const status = git(["status", "--porcelain", "-z"], dotDir);
12337
+ if (!status.ok) return [];
12338
+ const resolved = [];
12339
+ for (const entry of status.stdout.split("\0")) {
12340
+ if (entry.length < 4) continue;
12341
+ const xy = entry.slice(0, 2);
12342
+ const file = entry.slice(3);
12343
+ if (xy !== "DU" && xy !== "UD") continue;
12344
+ if (!file.startsWith("board/")) continue;
12345
+ const editedStage = xy === "DU" ? 3 : 2;
12346
+ const base = git(["show", `:1:${file}`], dotDir);
12347
+ const edited = git(["show", `:${editedStage}:${file}`], dotDir);
12348
+ const archived = path7.join(dotDir, "logs", path7.basename(file));
12349
+ if (edited.ok && fs7.existsSync(archived)) {
12350
+ const summary = summarizeFieldChanges(base.ok ? base.stdout : "", edited.stdout);
12351
+ const entryLine = `- ${(/* @__PURE__ */ new Date()).toISOString()}: [sync] edited elsewhere after completion${summary ? ` (${summary})` : ""}`;
12352
+ let content = fs7.readFileSync(archived, "utf-8");
12353
+ if (/^## Log\s*$/m.test(content)) {
12354
+ content = `${content.replace(/\s+$/, "")}
12355
+ ${entryLine}
12356
+ `;
12357
+ } else {
12358
+ content = `${content.replace(/\s+$/, "")}
12359
+
12360
+ ## Log
12361
+ ${entryLine}
12362
+ `;
12363
+ }
12364
+ fs7.writeFileSync(archived, content, "utf-8");
12365
+ git(["add", "--", path7.relative(dotDir, archived)], dotDir);
12366
+ }
12367
+ if (git(["rm", "--quiet", "--cached", "--", file], dotDir).ok) {
12368
+ try {
12369
+ fs7.rmSync(path7.join(dotDir, file), { force: true });
12370
+ } catch {
12371
+ }
12372
+ resolved.push(file);
12373
+ }
12374
+ }
12375
+ return resolved;
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
+ }
12407
+ function syncBoard(dotDir, options = {}) {
12408
+ const push = options.push ?? true;
12409
+ const result = { ok: true, pulled: 0, pushed: 0, conflicts: [] };
12410
+ if (!isTrackedBoard(dotDir)) return { ...result, skipped: "not-tracked" };
12411
+ const remote = boardRemote(dotDir);
12412
+ if (!remote) return { ...result, skipped: "no-remote" };
12413
+ const name = boardRemoteName(dotDir);
12414
+ const remoteRef = remoteBoardRef(name);
12415
+ const tracking = boardTrackingRef(remote, name);
12416
+ result.remote = remote;
12417
+ result.remoteRef = remoteRef;
12418
+ const checkLegacy = !legacyChecked(dotDir, remote);
12419
+ const pull = (options.pull ?? true) || checkLegacy;
12420
+ const stuck = unmergedFiles(dotDir);
12421
+ if (stuck.length > 0) {
12422
+ return finish(dotDir, { ...result, ok: false, failure: "conflict", conflicts: stuck, warning: `unresolved conflicts: ${stuck.join(", ")}` });
12423
+ }
12424
+ commitHandEdits(dotDir);
12425
+ if (ensureBoardAttributes(dotDir)) commitBoard(dotDir, { message: "chore: merge attributes" });
12426
+ registerMergeDriver(dotDir);
12427
+ let fetched = false;
12428
+ let legacy = null;
12429
+ let legacyAbsent = false;
12430
+ if (pull) {
12431
+ const fetch2 = git(["fetch", "--quiet", "--no-tags", remote, `+${remoteRef}:${tracking}`], dotDir);
12432
+ if (fetch2.ok) {
12433
+ fetched = true;
12434
+ } else if (!isMissingRemoteRef(fetch2.stderr)) {
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);
12438
+ }
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;
12446
+ }
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;
12453
+ }
12454
+ }
12455
+ if (push) {
12456
+ const known = git(["rev-parse", "--verify", "--quiet", tracking], dotDir).ok;
12457
+ const ahead = commitCount(dotDir, known ? `${tracking}..HEAD` : "HEAD");
12458
+ if (ahead > 0) {
12459
+ const pushed = git(["push", "--quiet", remote, `HEAD:${remoteRef}`], dotDir);
12460
+ if (!pushed.ok) {
12461
+ const rejected = /rejected|non-fast-forward|fetch first/i.test(pushed.stderr);
12462
+ return finish(dotDir, {
12463
+ ...result,
12464
+ ok: false,
12465
+ failure: rejected ? "rejected" : "offline",
12466
+ warning: rejected ? `push to ${remote} rejected (the remote moved) \u2014 run sync again` : `push to ${remote} failed: ${firstLine(pushed.stderr)}`
12467
+ });
12468
+ }
12469
+ git(["update-ref", tracking, "HEAD"], dotDir);
12470
+ result.pushed = ahead;
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);
12487
+ }
12488
+ return finish(dotDir, result);
12489
+ }
12490
+ function finish(dotDir, result) {
12491
+ writeSyncState(dotDir, {
12492
+ at: (/* @__PURE__ */ new Date()).toISOString(),
12493
+ remote: result.remote ?? "",
12494
+ remoteRef: result.remoteRef ?? "",
12495
+ ok: result.ok,
12496
+ ...result.warning ? { warning: result.warning } : {}
12497
+ });
12498
+ return result;
12499
+ }
12500
+ function secondsSinceSync(dotDir, now = Date.now()) {
12501
+ const state = readSyncState(dotDir);
12502
+ if (!state) return null;
12503
+ const at = Date.parse(state.at);
12504
+ return Number.isNaN(at) ? null : Math.max(0, Math.round((now - at) / 1e3));
12505
+ }
12506
+ function syncStatusLabel(dotDir, now = Date.now()) {
12507
+ if (!isTrackedBoard(dotDir)) return null;
12508
+ if (!boardRemote(dotDir)) return "local only";
12509
+ const state = readSyncState(dotDir);
12510
+ if (!state) return "not synced yet";
12511
+ if (!state.ok) return "not synced \xB7 saved locally";
12512
+ const seconds = secondsSinceSync(dotDir, now) ?? 0;
12513
+ const age = seconds < 60 ? `${seconds}s` : seconds < 3600 ? `${Math.round(seconds / 60)}m` : `${Math.round(seconds / 3600)}h`;
12514
+ return `synced ${age} ago`;
12515
+ }
12516
+ function pendingChanges(dotDir) {
12517
+ const remote = boardRemote(dotDir);
12518
+ if (!remote) return 0;
12519
+ const tracking = boardTrackingRef(remote, boardRemoteName(dotDir));
12520
+ const known = git(["rev-parse", "--verify", "--quiet", tracking], dotDir).ok;
12521
+ return commitCount(dotDir, known ? `${tracking}..HEAD` : "HEAD");
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
+ }
12528
+ var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
12529
+ function syncMessage(result) {
12530
+ if (result.skipped === "not-tracked") {
12531
+ return { tone: "warn", text: "This board is plain files on this machine and is not shared. To share it: brainfile migrate --to-branch" };
12532
+ }
12533
+ if (result.skipped === "no-remote") {
12534
+ return { tone: "warn", text: "This board is only on this machine. To share it: brainfile sync --set-remote origin" };
12535
+ }
12536
+ const remote = result.remote ?? "the remote";
12537
+ if (!result.ok) {
12538
+ switch (result.failure) {
12539
+ case "conflict":
12540
+ return {
12541
+ tone: "warn",
12542
+ text: `Two edits collided in ${result.conflicts.join(", ")}. Open the file in .brainfile/, keep the right version, then run brainfile sync. Nothing else is shared until then.`
12543
+ };
12544
+ case "rejected":
12545
+ return { tone: "warn", text: `Someone shared changes through ${remote} at the same moment. Run brainfile sync again to combine them. Nothing was lost.` };
12546
+ case "offline":
12547
+ return {
12548
+ tone: "warn",
12549
+ text: `Couldn't reach ${remote}. Nothing was lost: your changes are saved on this machine and will be sent on the next sync.`,
12550
+ detail: result.warning
12551
+ };
12552
+ default:
12553
+ return { tone: "warn", text: `Sync with ${remote} stopped. Your changes are saved on this machine.`, detail: result.warning };
12554
+ }
12555
+ }
12556
+ const parts = [];
12557
+ if (result.pulled > 0) parts.push(`received ${plural(result.pulled, "change")}`);
12558
+ if (result.pushed > 0) parts.push(`sent ${plural(result.pushed, "change")}`);
12559
+ return { tone: "ok", text: parts.length ? `Shared through ${remote}: ${parts.join(", ")}.` : `Up to date with ${remote}.` };
12560
+ }
12561
+
12562
+ // src/utils/board-autosync.ts
12563
+ import * as fs8 from "fs";
12564
+ import * as path8 from "path";
12565
+ import { spawn } from "child_process";
12566
+ var AUTOSYNC_DELAY_MS = 5e3;
12567
+ var FETCH_MAX_AGE_SECONDS = 60;
12568
+ var LOCK_FILE = path8.join("state", "autosync.lock");
12569
+ var LOCK_STALE_MS = 6e4;
12570
+ function effectiveAutosync(dotDir) {
12571
+ const override = process.env.BRAINFILE_AUTOSYNC;
12572
+ if (override === "off" || override === "push" || override === "full") return override;
12573
+ return boardAutosync(dotDir);
12574
+ }
12575
+ function autosyncCliPath() {
12576
+ const candidates = [
12577
+ path8.join(__dirname, "cli.js"),
12578
+ path8.join(__dirname, "..", "cli.js"),
12579
+ path8.join(__dirname, "..", "..", "dist", "cli.js")
12580
+ ];
12581
+ return candidates.find((p) => fs8.existsSync(p)) ?? null;
12582
+ }
12583
+ function pidAlive(pid) {
12584
+ try {
12585
+ process.kill(pid, 0);
12586
+ return true;
12587
+ } catch {
12588
+ return false;
12589
+ }
12590
+ }
12591
+ function pushPending(dotDir) {
12592
+ const lock = path8.join(dotDir, LOCK_FILE);
12593
+ try {
12594
+ const st = fs8.statSync(lock);
12595
+ if (Date.now() - st.mtimeMs > LOCK_STALE_MS) return false;
12596
+ const pid = Number.parseInt(fs8.readFileSync(lock, "utf-8"), 10);
12597
+ return Number.isFinite(pid) && pidAlive(pid);
12598
+ } catch {
12599
+ return false;
12600
+ }
12601
+ }
12602
+ function clearPushLock(dotDir) {
12603
+ try {
12604
+ fs8.rmSync(path8.join(dotDir, LOCK_FILE), { force: true });
12605
+ } catch {
12606
+ }
12607
+ }
12608
+ function schedulePush(dotDir, delayMs = AUTOSYNC_DELAY_MS) {
12609
+ if (!isTrackedBoard(dotDir)) return false;
12610
+ const mode = effectiveAutosync(dotDir);
12611
+ if (mode === "off" || !boardRemote(dotDir)) return false;
12612
+ if (pushPending(dotDir)) return false;
12613
+ const cli = autosyncCliPath();
12614
+ if (!cli) return false;
12615
+ const delay = process.env.BRAINFILE_AUTOSYNC_DELAY_MS ? Number(process.env.BRAINFILE_AUTOSYNC_DELAY_MS) : delayMs;
12616
+ try {
12617
+ const child = spawn(
12618
+ process.execPath,
12619
+ [cli, "sync", "--push", "--file", path8.join(dotDir, "brainfile.md"), "--wait", String(delay)],
12620
+ { cwd: dotDir, detached: true, stdio: "ignore", env: { ...process.env, BRAINFILE_AUTOSYNC: "off" } }
12621
+ );
12622
+ child.unref();
12623
+ if (child.pid) {
12624
+ fs8.mkdirSync(path8.join(dotDir, "state"), { recursive: true });
12625
+ fs8.writeFileSync(path8.join(dotDir, LOCK_FILE), String(child.pid), "utf-8");
12626
+ }
12627
+ return true;
12628
+ } catch {
12629
+ return false;
12630
+ }
12631
+ }
12632
+ function fetchBeforeRead(dotDir, maxAgeSeconds = FETCH_MAX_AGE_SECONDS) {
12633
+ if (!isTrackedBoard(dotDir)) return null;
12634
+ if (effectiveAutosync(dotDir) !== "full") return null;
12635
+ const age = secondsSinceSync(dotDir);
12636
+ if (age !== null && age < maxAgeSeconds) return null;
12637
+ return syncBoard(dotDir, { pull: true, push: false });
12638
+ }
12639
+ var installed = false;
12640
+ function installAutosync() {
12641
+ if (installed) return;
12642
+ installed = true;
12643
+ onBoardCommitted((dotDir) => {
12644
+ schedulePush(dotDir);
12645
+ });
11656
12646
  }
11657
12647
 
11658
12648
  // src/utils/config.ts
11659
- import * as fs5 from "fs";
11660
- import * as path5 from "path";
11661
- import * as os from "os";
12649
+ import * as fs9 from "fs";
12650
+ import * as path9 from "path";
12651
+ import * as os4 from "os";
11662
12652
  function resolveHomeDir() {
11663
12653
  const envHome = process.env.XDG_CONFIG_HOME?.trim();
11664
12654
  if (envHome) {
11665
12655
  return envHome;
11666
12656
  }
11667
12657
  const home = process.env.HOME?.trim();
11668
- return home || os.homedir();
12658
+ return home || os4.homedir();
11669
12659
  }
11670
12660
  function getConfigDir() {
11671
12661
  const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim();
11672
12662
  if (xdgConfigHome) {
11673
- return path5.join(xdgConfigHome, "brainfile");
12663
+ return path9.join(xdgConfigHome, "brainfile");
11674
12664
  }
11675
- return path5.join(resolveHomeDir(), ".config", "brainfile");
12665
+ return path9.join(resolveHomeDir(), ".config", "brainfile");
11676
12666
  }
11677
12667
  function getConfigPath() {
11678
- return path5.join(getConfigDir(), "config.json");
12668
+ return path9.join(getConfigDir(), "config.json");
11679
12669
  }
11680
12670
  function ensureConfigDir() {
11681
12671
  const configDir = getConfigDir();
11682
- if (!fs5.existsSync(configDir)) {
11683
- fs5.mkdirSync(configDir, { recursive: true, mode: 448 });
12672
+ if (!fs9.existsSync(configDir)) {
12673
+ fs9.mkdirSync(configDir, { recursive: true, mode: 448 });
11684
12674
  }
11685
12675
  }
11686
12676
  function loadConfig() {
11687
12677
  const configFile = getConfigPath();
11688
12678
  try {
11689
- if (fs5.existsSync(configFile)) {
11690
- const content = fs5.readFileSync(configFile, "utf-8");
12679
+ if (fs9.existsSync(configFile)) {
12680
+ const content = fs9.readFileSync(configFile, "utf-8");
11691
12681
  return JSON.parse(content);
11692
12682
  }
11693
12683
  } catch (error) {
@@ -11698,7 +12688,7 @@ function loadConfig() {
11698
12688
  function saveConfig(config) {
11699
12689
  const configFile = getConfigPath();
11700
12690
  ensureConfigDir();
11701
- fs5.writeFileSync(configFile, JSON.stringify(config, null, 2), {
12691
+ fs9.writeFileSync(configFile, JSON.stringify(config, null, 2), {
11702
12692
  encoding: "utf-8",
11703
12693
  mode: 384
11704
12694
  // Owner read/write only
@@ -11773,11 +12763,11 @@ function isValidDestination(dest) {
11773
12763
  }
11774
12764
 
11775
12765
  // src/utils/auth-store.ts
11776
- import * as fs6 from "fs";
11777
- import * as path6 from "path";
12766
+ import * as fs10 from "fs";
12767
+ import * as path10 from "path";
11778
12768
  import { execSync } from "child_process";
11779
12769
  var SERVICE_NAME = "brainfile";
11780
- var AUTH_FILE = path6.join(getConfigDir(), "auth.json");
12770
+ var AUTH_FILE = path10.join(getConfigDir(), "auth.json");
11781
12771
  var ENV_VAR_MAP = {
11782
12772
  github: ["GITHUB_TOKEN", "GH_TOKEN"],
11783
12773
  linear: ["LINEAR_API_KEY", "LINEAR_TOKEN"]
@@ -11947,8 +12937,8 @@ var AuthStore = class {
11947
12937
  }
11948
12938
  loadAuthFile() {
11949
12939
  try {
11950
- if (fs6.existsSync(AUTH_FILE)) {
11951
- const content = fs6.readFileSync(AUTH_FILE, "utf-8");
12940
+ if (fs10.existsSync(AUTH_FILE)) {
12941
+ const content = fs10.readFileSync(AUTH_FILE, "utf-8");
11952
12942
  return JSON.parse(content);
11953
12943
  }
11954
12944
  } catch {
@@ -11957,7 +12947,7 @@ var AuthStore = class {
11957
12947
  }
11958
12948
  saveAuthFile(data) {
11959
12949
  ensureConfigDir();
11960
- fs6.writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), {
12950
+ fs10.writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), {
11961
12951
  encoding: "utf-8",
11962
12952
  mode: 384
11963
12953
  // Owner read/write only
@@ -12205,7 +13195,7 @@ async function logoutGitHub() {
12205
13195
  await authStore.clear("github");
12206
13196
  }
12207
13197
  function sleep(ms) {
12208
- return new Promise((resolve5) => setTimeout(resolve5, ms));
13198
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
12209
13199
  }
12210
13200
 
12211
13201
  // src/utils/linear-auth.ts
@@ -12552,12 +13542,51 @@ export {
12552
13542
  renderCliError,
12553
13543
  V1_UNSUPPORTED_MESSAGE,
12554
13544
  assertV2Brainfile,
13545
+ git,
13546
+ gitToplevel,
13547
+ listWorktrees,
13548
+ boardBranch,
13549
+ boardRepoKind,
13550
+ isTrackedBoard,
13551
+ findTrackedBoardDir,
13552
+ materializeBoardWorktree,
13553
+ ensureExcludeEntry,
13554
+ removeExcludeEntry,
13555
+ createOrphanBoardWorktree,
13556
+ initStandaloneBoardRepo,
13557
+ boardIsDirty,
13558
+ commitBoard,
13559
+ commitHandEdits,
13560
+ homeBoardDir,
13561
+ isGlobalBoardRequested,
13562
+ describeCommandForCommit,
13563
+ scheduleBoardCommit,
12555
13564
  resolveCliBrainfilePath,
13565
+ tryResolveBoardDir,
12556
13566
  workspaceRootFromBrainfilePath,
12557
13567
  probeWorkspaceFormat,
12558
13568
  shouldSuggestV2Migration,
12559
13569
  markV2MigrationHintShown,
12560
13570
  import_core5 as import_core,
13571
+ mergeDriverCommand,
13572
+ boardRemote,
13573
+ boardRemoteRef,
13574
+ boardAutosync,
13575
+ setBoardAutosync,
13576
+ setBoardRemote,
13577
+ registerMergeDriver,
13578
+ ensureBoardAttributes,
13579
+ readSyncState,
13580
+ syncBoard,
13581
+ secondsSinceSync,
13582
+ syncStatusLabel,
13583
+ pendingChanges,
13584
+ boardWebUrl,
13585
+ syncMessage,
13586
+ effectiveAutosync,
13587
+ clearPushLock,
13588
+ fetchBeforeRead,
13589
+ installAutosync,
12561
13590
  getConfigPath,
12562
13591
  loadConfig,
12563
13592
  saveConfig,