brainfile 0.20.0 → 0.21.0

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 (83) hide show
  1. package/dist/{chunk-UOCQBEEW.mjs → chunk-FOLBAGJS.mjs} +1232 -318
  2. package/dist/cli.mjs +2048 -1318
  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/mcp.d.ts.map +1 -1
  12. package/dist/commands/mcp.js +44 -0
  13. package/dist/commands/mcp.js.map +1 -1
  14. package/dist/commands/merge-driver.d.ts +26 -0
  15. package/dist/commands/merge-driver.d.ts.map +1 -0
  16. package/dist/commands/merge-driver.js +195 -0
  17. package/dist/commands/merge-driver.js.map +1 -0
  18. package/dist/commands/migrate-tracked.d.ts +8 -0
  19. package/dist/commands/migrate-tracked.d.ts.map +1 -0
  20. package/dist/commands/migrate-tracked.js +428 -0
  21. package/dist/commands/migrate-tracked.js.map +1 -0
  22. package/dist/commands/migrate.d.ts +6 -0
  23. package/dist/commands/migrate.d.ts.map +1 -1
  24. package/dist/commands/migrate.js +9 -0
  25. package/dist/commands/migrate.js.map +1 -1
  26. package/dist/commands/sync.d.ts +16 -0
  27. package/dist/commands/sync.d.ts.map +1 -0
  28. package/dist/commands/sync.js +152 -0
  29. package/dist/commands/sync.js.map +1 -0
  30. package/dist/commands/where.d.ts +21 -0
  31. package/dist/commands/where.d.ts.map +1 -0
  32. package/dist/commands/where.js +135 -0
  33. package/dist/commands/where.js.map +1 -0
  34. package/dist/mcp/helpers.d.ts +8 -0
  35. package/dist/mcp/helpers.d.ts.map +1 -1
  36. package/dist/mcp/helpers.js +44 -0
  37. package/dist/mcp/helpers.js.map +1 -1
  38. package/dist/mcp/tools/brief_tool.d.ts.map +1 -1
  39. package/dist/mcp/tools/brief_tool.js +39 -1
  40. package/dist/mcp/tools/brief_tool.js.map +1 -1
  41. package/dist/mcp/tools/contract_tool.d.ts.map +1 -1
  42. package/dist/mcp/tools/contract_tool.js +6 -5
  43. package/dist/mcp/tools/contract_tool.js.map +1 -1
  44. package/dist/mcp/tools/subtask_tool.d.ts.map +1 -1
  45. package/dist/mcp/tools/subtask_tool.js +5 -4
  46. package/dist/mcp/tools/subtask_tool.js.map +1 -1
  47. package/dist/mcp/tools/task_add_tool.d.ts.map +1 -1
  48. package/dist/mcp/tools/task_add_tool.js +6 -5
  49. package/dist/mcp/tools/task_add_tool.js.map +1 -1
  50. package/dist/mcp/tools/task_complete_tool.d.ts.map +1 -1
  51. package/dist/mcp/tools/task_complete_tool.js +5 -4
  52. package/dist/mcp/tools/task_complete_tool.js.map +1 -1
  53. package/dist/mcp/tools/task_delete_tool.d.ts.map +1 -1
  54. package/dist/mcp/tools/task_delete_tool.js +5 -4
  55. package/dist/mcp/tools/task_delete_tool.js.map +1 -1
  56. package/dist/mcp/tools/task_move_tool.d.ts.map +1 -1
  57. package/dist/mcp/tools/task_move_tool.js +6 -5
  58. package/dist/mcp/tools/task_move_tool.js.map +1 -1
  59. package/dist/mcp/tools/task_patch_tool.d.ts.map +1 -1
  60. package/dist/mcp/tools/task_patch_tool.js +5 -4
  61. package/dist/mcp/tools/task_patch_tool.js.map +1 -1
  62. package/dist/{tui-H3RI22S4.mjs → tui-M2KWIG2E.mjs} +295 -253
  63. package/dist/utils/board-autosync.d.ts +22 -0
  64. package/dist/utils/board-autosync.d.ts.map +1 -0
  65. package/dist/utils/board-autosync.js +166 -0
  66. package/dist/utils/board-autosync.js.map +1 -0
  67. package/dist/utils/board-commit-hooks.d.ts +12 -0
  68. package/dist/utils/board-commit-hooks.d.ts.map +1 -0
  69. package/dist/utils/board-commit-hooks.js +51 -0
  70. package/dist/utils/board-commit-hooks.js.map +1 -0
  71. package/dist/utils/board-repo.d.ts +108 -0
  72. package/dist/utils/board-repo.d.ts.map +1 -0
  73. package/dist/utils/board-repo.js +501 -0
  74. package/dist/utils/board-repo.js.map +1 -0
  75. package/dist/utils/board-sync.d.ts +111 -0
  76. package/dist/utils/board-sync.d.ts.map +1 -0
  77. package/dist/utils/board-sync.js +469 -0
  78. package/dist/utils/board-sync.js.map +1 -0
  79. package/dist/utils/brainfile-path.d.ts +11 -1
  80. package/dist/utils/brainfile-path.d.ts.map +1 -1
  81. package/dist/utils/brainfile-path.js +39 -2
  82. package/dist/utils/brainfile-path.js.map +1 -1
  83. 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) {
@@ -1003,7 +1003,7 @@ var require_ansi_styles = __commonJS({
1003
1003
  });
1004
1004
  };
1005
1005
  var colorConvert;
1006
- var makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => {
1006
+ var makeDynamicStyles = (wrap, targetSpace, identity2, isBackground) => {
1007
1007
  if (colorConvert === void 0) {
1008
1008
  colorConvert = require_color_convert();
1009
1009
  }
@@ -1012,7 +1012,7 @@ var require_ansi_styles = __commonJS({
1012
1012
  for (const [sourceSpace, suite] of Object.entries(colorConvert)) {
1013
1013
  const name = sourceSpace === "ansi16" ? "ansi" : sourceSpace;
1014
1014
  if (sourceSpace === targetSpace) {
1015
- styles[name] = wrap(identity, offset);
1015
+ styles[name] = wrap(identity2, offset);
1016
1016
  } else if (typeof suite === "object") {
1017
1017
  styles[name] = wrap(suite[targetSpace], offset);
1018
1018
  }
@@ -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,415 @@ 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 EXCLUDE_ENTRY = ".brainfile/";
11553
+ var EXCLUDE_MARKER = "# brainfile: board worktree (managed by `brainfile`)";
11554
+ var FALLBACK_IDENTITY_NAME = "brainfile";
11555
+ var FALLBACK_IDENTITY_EMAIL = "brainfile@localhost";
11556
+ var MAX_MESSAGE_LENGTH = 72;
11557
+ var BOARD_SUBDIRS = ["board", "logs"];
11558
+ function git(args, cwd, env) {
11559
+ const result = spawnSync("git", args, {
11560
+ cwd,
11561
+ encoding: "utf-8",
11562
+ env: env ? { ...process.env, ...env } : process.env,
11563
+ stdio: ["ignore", "pipe", "pipe"]
11564
+ });
11565
+ return {
11566
+ ok: result.status === 0,
11567
+ status: result.status,
11568
+ stdout: (result.stdout ?? "").toString().trim(),
11569
+ stderr: (result.stderr ?? "").toString().trim()
11570
+ };
11571
+ }
11572
+ function gitToplevel(cwd) {
11573
+ const r = git(["rev-parse", "--show-toplevel"], cwd);
11574
+ return r.ok && r.stdout ? path2.resolve(r.stdout) : null;
11575
+ }
11576
+ function gitCommonDir(cwd) {
11577
+ const r = git(["rev-parse", "--git-common-dir"], cwd);
11578
+ if (!r.ok || !r.stdout) return null;
11579
+ return path2.resolve(cwd, r.stdout);
11580
+ }
11581
+ function listWorktrees(cwd) {
11582
+ const r = git(["worktree", "list", "--porcelain"], cwd);
11583
+ if (!r.ok) return [];
11584
+ const entries = [];
11585
+ let current = null;
11586
+ for (const line of r.stdout.split("\n")) {
11587
+ if (line.startsWith("worktree ")) {
11588
+ current = { path: path2.resolve(line.slice("worktree ".length)), bare: false, detached: false };
11589
+ entries.push(current);
11590
+ } else if (!current) {
11591
+ continue;
11592
+ } else if (line.startsWith("HEAD ")) {
11593
+ current.head = line.slice(5);
11594
+ } else if (line.startsWith("branch ")) {
11595
+ current.branch = line.slice(7);
11596
+ } else if (line === "bare") {
11597
+ current.bare = true;
11598
+ } else if (line === "detached") {
11599
+ current.detached = true;
11600
+ }
11601
+ }
11602
+ return entries;
11603
+ }
11604
+ function boardBranch(cwd) {
11605
+ const r = git(["config", "--get", BOARD_BRANCH_CONFIG_KEY], cwd);
11606
+ return r.ok && r.stdout ? r.stdout : DEFAULT_BOARD_BRANCH;
11607
+ }
11608
+ function boardRepoKind(dotDir) {
11609
+ try {
11610
+ const st = fs2.statSync(path2.join(dotDir, ".git"));
11611
+ if (st.isFile()) return "linked";
11612
+ if (st.isDirectory()) return "standalone";
11613
+ } catch {
11614
+ }
11615
+ return null;
11616
+ }
11617
+ function isTrackedBoard(dotDir) {
11618
+ return boardRepoKind(dotDir) !== null;
11619
+ }
11620
+ function findTrackedBoardDir(cwd) {
11621
+ const worktrees = listWorktrees(cwd);
11622
+ if (worktrees.length === 0) return null;
11623
+ const wanted = `refs/heads/${boardBranch(cwd)}`;
11624
+ const hit = worktrees.find((w) => w.branch === wanted);
11625
+ if (!hit) return null;
11626
+ return fs2.existsSync(path2.join(hit.path, "brainfile.md")) ? hit.path : null;
11627
+ }
11628
+ function materializeBoardWorktree(cwd) {
11629
+ const branch = boardBranch(cwd);
11630
+ const hasLocal = git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], cwd).ok;
11631
+ const configuredRemote = git(["config", "--get", "brainfile.remote"], cwd).stdout || null;
11632
+ const configuredBranch = git(["config", "--get", "brainfile.remoteBranch"], cwd).stdout || branch;
11633
+ let upstream = null;
11634
+ if (!hasLocal) {
11635
+ if (configuredRemote) {
11636
+ const ref = `${configuredRemote}/${configuredBranch}`;
11637
+ if (!git(["rev-parse", "--verify", "--quiet", `refs/remotes/${ref}`], cwd).ok) {
11638
+ git(["fetch", "--quiet", configuredRemote, configuredBranch], cwd);
11639
+ }
11640
+ if (git(["rev-parse", "--verify", "--quiet", `refs/remotes/${ref}`], cwd).ok) upstream = ref;
11641
+ }
11642
+ if (!upstream && git(["rev-parse", "--verify", "--quiet", `refs/remotes/origin/${branch}`], cwd).ok) {
11643
+ upstream = `origin/${branch}`;
11644
+ }
11645
+ }
11646
+ if (!hasLocal && !upstream) return null;
11647
+ const worktrees = listWorktrees(cwd);
11648
+ if (worktrees.length === 0) return null;
11649
+ if (worktrees.some((w) => w.branch === `refs/heads/${branch}`)) return null;
11650
+ const main = worktrees[0];
11651
+ let target;
11652
+ if (main.bare) {
11653
+ const common = gitCommonDir(cwd);
11654
+ if (!common) return null;
11655
+ target = path2.join(common, "brainfile");
11656
+ } else {
11657
+ target = path2.join(main.path, ".brainfile");
11658
+ }
11659
+ if (fs2.existsSync(target)) return null;
11660
+ const r = hasLocal ? git(["worktree", "add", "--quiet", target, branch], cwd) : git(["worktree", "add", "--quiet", "--track", "-b", branch, target, upstream], cwd);
11661
+ if (!r.ok) return null;
11662
+ ensureExcludeEntry(cwd);
11663
+ for (const sub of BOARD_SUBDIRS) fs2.mkdirSync(path2.join(target, sub), { recursive: true });
11664
+ const cameFrom = upstream ? upstream.slice(0, upstream.indexOf("/")) : null;
11665
+ if (cameFrom && !configuredRemote) git(["config", "brainfile.remote", cameFrom], cwd);
11666
+ const relative3 = path2.relative(process.cwd(), target) || ".";
11667
+ process.stderr.write(
11668
+ cameFrom ? `Checked out the shared board from ${cameFrom} into ${relative3}/
11669
+ ` : `Checked out the board from the '${branch}' branch into ${relative3}/
11670
+ `
11671
+ );
11672
+ return target;
11673
+ }
11674
+ function ensureExcludeEntry(cwd) {
11675
+ const common = gitCommonDir(cwd);
11676
+ if (!common) return false;
11677
+ const file = path2.join(common, "info", "exclude");
11678
+ const existing = fs2.existsSync(file) ? fs2.readFileSync(file, "utf-8") : "";
11679
+ const present = existing.split(/\r?\n/).some((line) => line.trim() === EXCLUDE_ENTRY || line.trim() === ".brainfile");
11680
+ if (present) return false;
11681
+ fs2.mkdirSync(path2.dirname(file), { recursive: true });
11682
+ const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
11683
+ fs2.appendFileSync(file, `${separator}${EXCLUDE_MARKER}
11684
+ ${EXCLUDE_ENTRY}
11685
+ `, "utf-8");
11686
+ return true;
11687
+ }
11688
+ function removeExcludeEntry(cwd) {
11689
+ const common = gitCommonDir(cwd);
11690
+ if (!common) return false;
11691
+ const file = path2.join(common, "info", "exclude");
11692
+ if (!fs2.existsSync(file)) return false;
11693
+ const lines = fs2.readFileSync(file, "utf-8").split(/\r?\n/);
11694
+ const at = lines.indexOf(EXCLUDE_MARKER);
11695
+ if (at < 0) return false;
11696
+ const removeCount = lines[at + 1]?.trim() === EXCLUDE_ENTRY ? 2 : 1;
11697
+ lines.splice(at, removeCount);
11698
+ fs2.writeFileSync(file, lines.join("\n"), "utf-8");
11699
+ return true;
11700
+ }
11701
+ function createOrphanBoardWorktree(repoCwd, dotDir, branch) {
11702
+ const r = git(["worktree", "add", "--quiet", "--orphan", "-b", branch, dotDir], repoCwd);
11703
+ if (!r.ok) {
11704
+ throw new Error(
11705
+ `Could not create the board worktree on branch '${branch}' (git >= 2.42 is required for --orphan): ${r.stderr}`
11706
+ );
11707
+ }
11708
+ }
11709
+ function initStandaloneBoardRepo(dotDir) {
11710
+ const parent = path2.dirname(dotDir);
11711
+ let r = git(["init", "--quiet", "-b", "main", dotDir], parent);
11712
+ if (!r.ok) r = git(["init", "--quiet", dotDir], parent);
11713
+ if (!r.ok) throw new Error(`Could not initialize a git repository in ${dotDir}: ${r.stderr}`);
11714
+ }
11715
+ function boardIsDirty(dotDir) {
11716
+ const r = git(["status", "--porcelain"], dotDir);
11717
+ return r.ok && r.stdout.length > 0;
11718
+ }
11719
+ function unmergedFiles(dotDir) {
11720
+ const r = git(["diff", "--name-only", "--diff-filter=U"], dotDir);
11721
+ return r.ok ? r.stdout.split("\n").map((l) => l.trim()).filter(Boolean) : [];
11722
+ }
11723
+ function boardDirtyFiles(dotDir) {
11724
+ const r = git(["status", "--porcelain"], dotDir);
11725
+ if (!r.ok || !r.stdout) return [];
11726
+ return r.stdout.split("\n").map((line) => line.replace(/^[ MADRCU?!]{1,2}\s+/, "").replace(/^.* -> /, "").trim()).filter(Boolean);
11727
+ }
11728
+ function identityArgs(dotDir) {
11729
+ const args = [];
11730
+ if (!git(["config", "--get", "user.name"], dotDir).stdout) args.push("-c", `user.name=${FALLBACK_IDENTITY_NAME}`);
11731
+ if (!git(["config", "--get", "user.email"], dotDir).stdout) args.push("-c", `user.email=${FALLBACK_IDENTITY_EMAIL}`);
11732
+ return args;
11733
+ }
11734
+ function sanitizeAgent(agent) {
11735
+ return agent.replace(/[<>\r\n]/g, "").trim();
11736
+ }
11737
+ var commitListeners = [];
11738
+ function onBoardCommitted(listener) {
11739
+ if (!commitListeners.includes(listener)) commitListeners.push(listener);
11740
+ }
11741
+ function notifyCommitted(dotDir) {
11742
+ for (const listener of commitListeners) {
11743
+ try {
11744
+ listener(dotDir);
11745
+ } catch {
11746
+ }
11747
+ }
11748
+ }
11749
+ function ensureBoardDirs(dotDir) {
11750
+ let created = false;
11751
+ for (const sub of BOARD_SUBDIRS) {
11752
+ const dir = path2.join(dotDir, sub);
11753
+ try {
11754
+ fs2.mkdirSync(dir, { recursive: true });
11755
+ const keep = path2.join(dir, ".gitkeep");
11756
+ if (!fs2.existsSync(keep)) {
11757
+ fs2.writeFileSync(keep, "", "utf-8");
11758
+ created = true;
11759
+ }
11760
+ } catch {
11761
+ }
11762
+ }
11763
+ return created;
11764
+ }
11765
+ function commitBoard(dotDir, options) {
11766
+ if (!isTrackedBoard(dotDir)) return false;
11767
+ ensureBoardDirs(dotDir);
11768
+ if (unmergedFiles(dotDir).length > 0) return false;
11769
+ if (!git(["add", "-A"], dotDir).ok) return false;
11770
+ if (!boardIsDirty(dotDir)) return false;
11771
+ const env = {};
11772
+ const agent = options.agent ? sanitizeAgent(options.agent) : "";
11773
+ if (agent) {
11774
+ env.GIT_AUTHOR_NAME = agent;
11775
+ env.GIT_AUTHOR_EMAIL = `${agent}@brainfile.local`;
11776
+ }
11777
+ const message = oneLine(options.message);
11778
+ const r = git(
11779
+ [...identityArgs(dotDir), "-c", "commit.gpgsign=false", "commit", "--quiet", "--no-verify", "-m", message],
11780
+ dotDir,
11781
+ env
11782
+ );
11783
+ if (r.ok) notifyCommitted(dotDir);
11784
+ return r.ok;
11785
+ }
11786
+ function commitHandEdits(dotDir) {
11787
+ if (!isTrackedBoard(dotDir)) return false;
11788
+ const keepOnly = (f) => f.endsWith(".gitkeep") || BOARD_SUBDIRS.some((sub) => f === `${sub}/`);
11789
+ if (ensureBoardDirs(dotDir) && boardDirtyFiles(dotDir).every(keepOnly)) {
11790
+ commitBoard(dotDir, { message: "chore: keep board/ and logs/ in git" });
11791
+ }
11792
+ const files = boardDirtyFiles(dotDir);
11793
+ if (files.length === 0) return false;
11794
+ const summary = files.length <= 3 ? files.join(", ") : `${files.length} files`;
11795
+ return commitBoard(dotDir, { message: `edit: ${summary}` });
11796
+ }
11797
+ function resolveAgentName(argv = process.argv) {
11798
+ if (process.env.BRAINFILE_AGENT) return process.env.BRAINFILE_AGENT;
11799
+ const flag = argv.indexOf("--agent");
11800
+ if (flag >= 0 && argv[flag + 1] && !argv[flag + 1].startsWith("-")) return argv[flag + 1];
11801
+ const inline = argv.find((a) => a.startsWith("--agent="));
11802
+ return inline ? inline.slice("--agent=".length) : null;
11803
+ }
11804
+ function homeBoardDir() {
11805
+ return path2.join(os.homedir(), ".brainfile");
11806
+ }
11807
+ function isGlobalBoardRequested(argv = process.argv) {
11808
+ if (process.env.BRAINFILE_GLOBAL === "1") return true;
11809
+ return argv.includes("-g") || argv.includes("--global");
11810
+ }
11811
+ function oneLine(message) {
11812
+ const flat = message.replace(/\s+/g, " ").trim();
11813
+ return flat.length > MAX_MESSAGE_LENGTH ? `${flat.slice(0, MAX_MESSAGE_LENGTH - 1)}\u2026` : flat;
11814
+ }
11815
+ function describeCommandForCommit(command) {
11816
+ const names = [];
11817
+ let cursor = command;
11818
+ while (cursor && cursor.parent) {
11819
+ names.unshift(cursor.name());
11820
+ cursor = cursor.parent;
11821
+ }
11822
+ const opts = command.opts();
11823
+ const idValue = opts.task ?? opts.taskId;
11824
+ const id = Array.isArray(idValue) ? idValue.join(",") : typeof idValue === "string" ? idValue : "";
11825
+ const positional = command.args.filter((a) => typeof a === "string" && a.length > 0);
11826
+ const detail = [opts.title, opts.column, positional.join(" ")].find((v) => typeof v === "string" && v.length > 0) ?? "";
11827
+ const head = `${names.join(" ")}${id ? ` ${id}` : ""}`;
11828
+ return oneLine(detail ? `${head}: ${detail}` : head);
11829
+ }
11830
+ var pendingCommits = /* @__PURE__ */ new Map();
11831
+ var flushTimer = null;
11832
+ var exitHookInstalled = false;
11833
+ function scheduleBoardCommit(brainfilePath, message, delayMs = 2e3) {
11834
+ const dotDir = path2.dirname(path2.resolve(brainfilePath));
11835
+ if (!isTrackedBoard(dotDir)) return;
11836
+ const entry = pendingCommits.get(dotDir) ?? { messages: [], agent: resolveAgentName() };
11837
+ entry.messages.push(message);
11838
+ pendingCommits.set(dotDir, entry);
11839
+ if (flushTimer) clearTimeout(flushTimer);
11840
+ flushTimer = setTimeout(flushBoardCommits, delayMs);
11841
+ flushTimer.unref?.();
11842
+ if (!exitHookInstalled) {
11843
+ exitHookInstalled = true;
11844
+ process.once("exit", flushBoardCommits);
11845
+ }
11846
+ }
11847
+ function flushBoardCommits() {
11848
+ if (flushTimer) {
11849
+ clearTimeout(flushTimer);
11850
+ flushTimer = null;
11851
+ }
11852
+ for (const [dotDir, entry] of pendingCommits) {
11853
+ const [first, ...rest] = entry.messages;
11854
+ const message = rest.length === 0 ? first : `${first} (+${rest.length} more)`;
11855
+ commitBoard(dotDir, { message, agent: entry.agent });
11856
+ }
11857
+ pendingCommits.clear();
11858
+ }
11859
+
11533
11860
  // src/utils/brainfile-path.ts
11534
11861
  function isMigrationCommand() {
11535
11862
  return process.argv.includes("migrate");
11536
11863
  }
11537
11864
  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);
11865
+ const absolute = path3.resolve(brainfilePath);
11866
+ const dir = path3.basename(path3.dirname(absolute)) === ".brainfile" ? path3.dirname(path3.dirname(absolute)) : path3.dirname(absolute);
11540
11867
  return `Run: brainfile migrate --dir ${dir}`;
11541
11868
  }
11542
11869
  function rejectLegacyRuntimePath(resolvedPath) {
11543
- if (!isMigrationCommand() && fs2.existsSync(resolvedPath) && !(0, import_core3.isV2)(resolvedPath)) {
11870
+ if (!isMigrationCommand() && fs3.existsSync(resolvedPath) && !(0, import_core3.isV2)(resolvedPath)) {
11544
11871
  throw new CLIError(V1_UNSUPPORTED_MESSAGE, void 0, migrationHintForPath2(resolvedPath));
11545
11872
  }
11546
11873
  return resolvedPath;
11547
11874
  }
11875
+ function isPlaceholder(filePath) {
11876
+ return filePath === void 0 || filePath === import_core3.BRAINFILE_BASENAME || filePath === `./${import_core3.BRAINFILE_BASENAME}`;
11877
+ }
11548
11878
  function resolveCliBrainfilePath(filePath) {
11879
+ if (isPlaceholder(filePath) && isGlobalBoardRequested()) {
11880
+ return rejectLegacyRuntimePath(path3.join(homeBoardDir(), import_core3.BRAINFILE_BASENAME));
11881
+ }
11549
11882
  if (filePath) {
11550
- const resolved = path2.resolve(process.cwd(), filePath);
11883
+ const resolved = path3.resolve(process.cwd(), filePath);
11551
11884
  try {
11552
- if (fs2.statSync(resolved).isDirectory()) {
11885
+ if (fs3.statSync(resolved).isDirectory()) {
11553
11886
  const found = (0, import_core3.findBrainfile)(resolved);
11554
11887
  if (found && found.projectRoot === resolved) {
11555
11888
  return rejectLegacyRuntimePath(found.absolutePath);
11556
11889
  }
11557
- const dotDir = path2.join(resolved, ".brainfile", "brainfile.md");
11890
+ const dotDir = path3.join(resolved, ".brainfile", "brainfile.md");
11558
11891
  return rejectLegacyRuntimePath(dotDir);
11559
11892
  }
11560
11893
  } catch {
11561
11894
  }
11562
11895
  }
11563
- return rejectLegacyRuntimePath((0, import_core3.resolveBrainfilePath)({ filePath, startDir: process.cwd() }));
11896
+ const cwd = process.cwd();
11897
+ if (isPlaceholder(filePath)) {
11898
+ const found = (0, import_core3.findBrainfile)(cwd, { stopAtGitRoot: true });
11899
+ if (found) return rejectLegacyRuntimePath(found.absolutePath);
11900
+ const tracked = findTrackedBoardDir(cwd) ?? materializeBoardWorktree(cwd);
11901
+ if (tracked) return rejectLegacyRuntimePath(path3.join(tracked, import_core3.BRAINFILE_BASENAME));
11902
+ }
11903
+ return rejectLegacyRuntimePath((0, import_core3.resolveBrainfilePath)({ filePath, startDir: cwd, stopAtGitRoot: true }));
11904
+ }
11905
+ function tryResolveBoardDir(filePath) {
11906
+ try {
11907
+ const resolved = resolveCliBrainfilePath(filePath);
11908
+ if (!fs3.existsSync(resolved)) return null;
11909
+ return path3.dirname(resolved);
11910
+ } catch {
11911
+ return null;
11912
+ }
11564
11913
  }
11565
11914
 
11566
11915
  // src/utils/v2-detect.ts
11567
11916
  var import_core4 = __toESM(require_dist());
11568
11917
  var import_core5 = __toESM(require_dist());
11569
11918
  var import_core6 = __toESM(require_dist());
11570
- import * as fs4 from "fs";
11571
- import * as path4 from "path";
11919
+ import * as fs5 from "fs";
11920
+ import * as path5 from "path";
11572
11921
 
11573
11922
  // src/utils/workspace-format.ts
11574
- import * as fs3 from "fs";
11575
- import * as path3 from "path";
11923
+ import * as fs4 from "fs";
11924
+ import * as path4 from "path";
11576
11925
  function existsFile(filePath) {
11577
11926
  try {
11578
- return fs3.statSync(filePath).isFile();
11927
+ return fs4.statSync(filePath).isFile();
11579
11928
  } catch {
11580
11929
  return false;
11581
11930
  }
11582
11931
  }
11583
11932
  function existsDir(dirPath) {
11584
11933
  try {
11585
- return fs3.statSync(dirPath).isDirectory();
11934
+ return fs4.statSync(dirPath).isDirectory();
11586
11935
  } catch {
11587
11936
  return false;
11588
11937
  }
11589
11938
  }
11590
11939
  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;
11940
+ const absolute = path4.resolve(brainfilePath);
11941
+ const dir = path4.dirname(absolute);
11942
+ return path4.basename(dir) === ".brainfile" ? path4.dirname(dir) : dir;
11594
11943
  }
11595
11944
  function probeWorkspaceFormat(rootDir = process.cwd()) {
11596
- const resolvedRoot = path3.resolve(rootDir);
11597
- const dotDir = path3.join(resolvedRoot, ".brainfile");
11945
+ const resolvedRoot = path4.resolve(rootDir);
11946
+ const dotDir = path4.join(resolvedRoot, ".brainfile");
11598
11947
  const paths = {
11599
11948
  rootDir: resolvedRoot,
11600
- rootBrainfilePath: path3.join(resolvedRoot, "brainfile.md"),
11949
+ rootBrainfilePath: path4.join(resolvedRoot, "brainfile.md"),
11601
11950
  dotDir,
11602
- dotBrainfilePath: path3.join(dotDir, "brainfile.md"),
11603
- boardDir: path3.join(dotDir, "board"),
11604
- logsDir: path3.join(dotDir, "logs")
11951
+ dotBrainfilePath: path4.join(dotDir, "brainfile.md"),
11952
+ boardDir: path4.join(dotDir, "board"),
11953
+ logsDir: path4.join(dotDir, "logs")
11605
11954
  };
11606
11955
  const presence = {
11607
11956
  rootBrainfile: existsFile(paths.rootBrainfilePath),
@@ -11635,15 +11984,15 @@ function shouldSuggestMigration(probe) {
11635
11984
  // src/utils/v2-detect.ts
11636
11985
  var v2MigrationHintShown = /* @__PURE__ */ new Set();
11637
11986
  function shouldSuggestV2Migration(brainfilePath) {
11638
- const resolved = path4.resolve(brainfilePath);
11987
+ const resolved = path5.resolve(brainfilePath);
11639
11988
  if (v2MigrationHintShown.has(resolved)) return false;
11640
11989
  const probe = probeWorkspaceForBrainfile(resolved);
11641
11990
  if (shouldSuggestMigration(probe)) {
11642
11991
  return true;
11643
11992
  }
11644
- if (!(0, import_core6.isV2)(resolved) && fs4.existsSync(resolved)) {
11993
+ if (!(0, import_core6.isV2)(resolved) && fs5.existsSync(resolved)) {
11645
11994
  try {
11646
- const content = fs4.readFileSync(resolved, "utf-8");
11995
+ const content = fs5.readFileSync(resolved, "utf-8");
11647
11996
  const parsed = import_core4.Brainfile.parseWithErrors(content);
11648
11997
  if (parsed.board) return true;
11649
11998
  } catch {
@@ -11652,42 +12001,568 @@ function shouldSuggestV2Migration(brainfilePath) {
11652
12001
  return false;
11653
12002
  }
11654
12003
  function markV2MigrationHintShown(brainfilePath) {
11655
- v2MigrationHintShown.add(path4.resolve(brainfilePath));
12004
+ v2MigrationHintShown.add(path5.resolve(brainfilePath));
12005
+ }
12006
+
12007
+ // src/commands/merge-driver.ts
12008
+ var import_core7 = __toESM(require_dist());
12009
+ import * as fs6 from "fs";
12010
+ import * as os2 from "os";
12011
+ import * as path6 from "path";
12012
+ import { spawnSync as spawnSync2 } from "child_process";
12013
+ function parseDoc(content) {
12014
+ const parsed = (0, import_core7.parseFrontmatter)(content);
12015
+ if (!parsed) return null;
12016
+ return { data: parsed.data, body: parsed.body };
12017
+ }
12018
+ function same(a, b) {
12019
+ return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
12020
+ }
12021
+ function stamp(data) {
12022
+ const value = data.updatedAt;
12023
+ return typeof value === "string" ? value : "";
12024
+ }
12025
+ function mergeFields(base, ours, theirs) {
12026
+ const keys = [];
12027
+ for (const key of [...Object.keys(ours), ...Object.keys(theirs), ...Object.keys(base)]) {
12028
+ if (!keys.includes(key)) keys.push(key);
12029
+ }
12030
+ const oursWins = stamp(ours) > stamp(theirs);
12031
+ const out = {};
12032
+ for (const key of keys) {
12033
+ const b = base[key];
12034
+ const o = ours[key];
12035
+ const t = theirs[key];
12036
+ let pick;
12037
+ if (same(o, t)) pick = o;
12038
+ else if (same(b, o)) pick = t;
12039
+ else if (same(b, t)) pick = o;
12040
+ else if (key === "updatedAt") pick = String(o ?? "") > String(t ?? "") ? o : t;
12041
+ else pick = oursWins ? o : t;
12042
+ if (pick !== void 0) out[key] = pick;
12043
+ }
12044
+ return out;
12045
+ }
12046
+ var CONFLICT = /^<{7}[^\n]*\n([\s\S]*?)^={7}\n([\s\S]*?)^>{7}[^\n]*\n?/gm;
12047
+ var LIST_ITEM = /^\s*[-*]\s+/;
12048
+ var LEADING_STAMP = /^\s*[-*]\s+(\d{4}-\d{2}-\d{2}T[0-9:.]+Z?)\b/;
12049
+ function unionLists(ours, theirs) {
12050
+ const a = ours.split("\n").filter((l) => l.trim().length > 0);
12051
+ const b = theirs.split("\n").filter((l) => l.trim().length > 0);
12052
+ if (![...a, ...b].every((l) => LIST_ITEM.test(l))) return null;
12053
+ const merged = [...a];
12054
+ for (const line of b) if (!merged.includes(line)) merged.push(line);
12055
+ if (merged.every((l) => LEADING_STAMP.test(l))) {
12056
+ merged.sort((x, y) => {
12057
+ const sx = LEADING_STAMP.exec(x)?.[1] ?? "";
12058
+ const sy = LEADING_STAMP.exec(y)?.[1] ?? "";
12059
+ return sx < sy ? -1 : sx > sy ? 1 : 0;
12060
+ });
12061
+ }
12062
+ return `${merged.join("\n")}
12063
+ `;
12064
+ }
12065
+ function mergeBodies(base, ours, theirs) {
12066
+ if (ours === theirs) return { content: ours, clean: true };
12067
+ if (base === ours) return { content: theirs, clean: true };
12068
+ if (base === theirs) return { content: ours, clean: true };
12069
+ const dir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "brainfile-merge-"));
12070
+ try {
12071
+ const files = { base: path6.join(dir, "base"), ours: path6.join(dir, "ours"), theirs: path6.join(dir, "theirs") };
12072
+ fs6.writeFileSync(files.base, base);
12073
+ fs6.writeFileSync(files.ours, ours);
12074
+ fs6.writeFileSync(files.theirs, theirs);
12075
+ const r = spawnSync2("git", ["merge-file", "-p", "-L", "ours", "-L", "base", "-L", "theirs", files.ours, files.base, files.theirs], {
12076
+ encoding: "utf-8",
12077
+ stdio: ["ignore", "pipe", "pipe"]
12078
+ });
12079
+ if (r.status === null || r.status < 0) {
12080
+ return { content: `<<<<<<< ours
12081
+ ${ours}=======
12082
+ ${theirs}>>>>>>> theirs
12083
+ `, clean: false };
12084
+ }
12085
+ if (r.status === 0) return { content: r.stdout, clean: true };
12086
+ let clean = true;
12087
+ const content = r.stdout.replace(CONFLICT, (whole, oursHunk, theirsHunk) => {
12088
+ const union = unionLists(oursHunk, theirsHunk);
12089
+ if (union === null) {
12090
+ clean = false;
12091
+ return whole;
12092
+ }
12093
+ return union;
12094
+ });
12095
+ return { content, clean };
12096
+ } finally {
12097
+ fs6.rmSync(dir, { recursive: true, force: true });
12098
+ }
12099
+ }
12100
+ function mergeBoardDocuments(base, ours, theirs) {
12101
+ const b = parseDoc(base);
12102
+ const o = parseDoc(ours);
12103
+ const t = parseDoc(theirs);
12104
+ if (!o || !t) return mergeBodies(base, ours, theirs);
12105
+ const data = mergeFields(b?.data ?? {}, o.data, t.data);
12106
+ const body = mergeBodies(b?.body ?? "", o.body, t.body);
12107
+ return { content: (0, import_core7.serializeFrontmatter)(data, body.content), clean: body.clean };
12108
+ }
12109
+ function summarizeFieldChanges(before, after) {
12110
+ const b = parseDoc(before)?.data ?? {};
12111
+ const a = parseDoc(after)?.data ?? {};
12112
+ const parts = [];
12113
+ for (const key of Object.keys(a)) {
12114
+ if (key === "updatedAt" || same(b[key], a[key])) continue;
12115
+ const render = (v) => typeof v === "string" ? v : JSON.stringify(v);
12116
+ parts.push(`${key}: ${b[key] === void 0 ? "\u2205" : render(b[key])} \u2192 ${render(a[key])}`);
12117
+ }
12118
+ return parts.join("; ");
12119
+ }
12120
+ function mergeDriverCommand(options) {
12121
+ const read = (p) => fs6.existsSync(p) ? fs6.readFileSync(p, "utf-8") : "";
12122
+ if (!options.ours || !options.theirs) return 2;
12123
+ const outcome = mergeBoardDocuments(read(options.base), read(options.ours), read(options.theirs));
12124
+ fs6.writeFileSync(options.ours, outcome.content, "utf-8");
12125
+ return outcome.clean ? 0 : 1;
12126
+ }
12127
+
12128
+ // src/utils/board-sync.ts
12129
+ import * as fs7 from "fs";
12130
+ import * as os3 from "os";
12131
+ import * as path7 from "path";
12132
+ var REMOTE_CONFIG_KEY = "brainfile.remote";
12133
+ var REMOTE_BRANCH_CONFIG_KEY = "brainfile.remoteBranch";
12134
+ var AUTOSYNC_CONFIG_KEY = "brainfile.autosync";
12135
+ var URL_REMOTE_NAME = "board";
12136
+ var MERGE_DRIVER_NAME = "brainfile";
12137
+ var DEFAULT_MERGE_DRIVER_COMMAND = "brainfile merge-driver %O %A %B";
12138
+ var SYNC_STATE_FILE = path7.join("state", "sync.json");
12139
+ var BOARD_GITATTRIBUTES = [
12140
+ "logs/ledger.jsonl merge=union",
12141
+ "board/*.md merge=brainfile",
12142
+ "logs/*.md merge=brainfile",
12143
+ "brainfile.md merge=brainfile"
12144
+ ];
12145
+ function configGet(dotDir, key) {
12146
+ const r = git(["config", "--get", key], dotDir);
12147
+ return r.ok && r.stdout ? r.stdout : null;
12148
+ }
12149
+ function configSet(dotDir, key, value) {
12150
+ return git(["config", key, value], dotDir).ok;
12151
+ }
12152
+ function boardRemote(dotDir) {
12153
+ return configGet(dotDir, REMOTE_CONFIG_KEY);
12154
+ }
12155
+ function boardRemoteBranch(dotDir) {
12156
+ const configured = configGet(dotDir, REMOTE_BRANCH_CONFIG_KEY);
12157
+ if (configured) return configured;
12158
+ if (path7.resolve(dotDir) === path7.resolve(homeBoardDir())) return "home";
12159
+ if (boardRepoKind(dotDir) === "standalone" && path7.basename(path7.resolve(dotDir)) === ".brainfile") {
12160
+ return path7.basename(path7.dirname(path7.resolve(dotDir))) || boardBranch(dotDir);
12161
+ }
12162
+ return boardBranch(dotDir);
12163
+ }
12164
+ function boardAutosync(dotDir) {
12165
+ const value = configGet(dotDir, AUTOSYNC_CONFIG_KEY);
12166
+ if (value === "off" || value === "push" || value === "full") return value;
12167
+ return boardRemote(dotDir) ? "push" : "off";
12168
+ }
12169
+ function setBoardAutosync(dotDir, mode) {
12170
+ return configSet(dotDir, AUTOSYNC_CONFIG_KEY, mode);
12171
+ }
12172
+ function looksLikeRemoteUrl(target) {
12173
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(target)) return true;
12174
+ if (/^[^/\s]+@[^/\s]+:/.test(target)) return true;
12175
+ if (target.startsWith("/") || target.startsWith(".") || target.startsWith("~")) return true;
12176
+ if (target.includes("/") && fs7.existsSync(target)) return true;
12177
+ return false;
12178
+ }
12179
+ function setBoardRemote(cwd, target, remoteBranch) {
12180
+ const trimmed = target.trim();
12181
+ if (!trimmed) throw new Error("remote is required");
12182
+ let remote;
12183
+ if (looksLikeRemoteUrl(trimmed)) {
12184
+ remote = URL_REMOTE_NAME;
12185
+ const url = trimmed.startsWith("~/") ? path7.join(os3.homedir(), trimmed.slice(2)) : trimmed;
12186
+ const exists = git(["remote", "get-url", remote], cwd).ok;
12187
+ const r = exists ? git(["remote", "set-url", remote, url], cwd) : git(["remote", "add", remote, url], cwd);
12188
+ if (!r.ok) throw new Error(r.stderr || `could not register remote '${remote}'`);
12189
+ } else {
12190
+ remote = trimmed;
12191
+ if (!git(["remote", "get-url", remote], cwd).ok) {
12192
+ const known = git(["remote"], cwd).stdout.split("\n").filter(Boolean);
12193
+ const hint = known.length ? ` Known remotes: ${known.join(", ")}.` : "";
12194
+ throw new Error(`'${remote}' is not a git remote here.${hint} Pass a URL to add one.`);
12195
+ }
12196
+ }
12197
+ configSet(cwd, REMOTE_CONFIG_KEY, remote);
12198
+ if (remoteBranch) configSet(cwd, REMOTE_BRANCH_CONFIG_KEY, remoteBranch);
12199
+ registerMergeDriver(cwd);
12200
+ return {
12201
+ remote,
12202
+ url: git(["remote", "get-url", remote], cwd).stdout || null,
12203
+ remoteBranch: remoteBranch ?? boardRemoteBranch(cwd)
12204
+ };
12205
+ }
12206
+ function registerMergeDriver(cwd, command) {
12207
+ const current = configGet(cwd, `merge.${MERGE_DRIVER_NAME}.driver`);
12208
+ if (command === void 0) {
12209
+ if (current) return false;
12210
+ command = DEFAULT_MERGE_DRIVER_COMMAND;
12211
+ }
12212
+ if (current === command) return false;
12213
+ configSet(cwd, `merge.${MERGE_DRIVER_NAME}.name`, "brainfile board merge (field-level, last writer wins)");
12214
+ return configSet(cwd, `merge.${MERGE_DRIVER_NAME}.driver`, command);
12215
+ }
12216
+ function ensureBoardAttributes(dotDir) {
12217
+ const file = path7.join(dotDir, ".gitattributes");
12218
+ let existing = "";
12219
+ try {
12220
+ existing = fs7.readFileSync(file, "utf-8");
12221
+ } catch {
12222
+ }
12223
+ const lines = existing.split("\n").map((l) => l.trim());
12224
+ const missing = BOARD_GITATTRIBUTES.filter((l) => !lines.includes(l));
12225
+ if (missing.length === 0) return false;
12226
+ const prefix = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
12227
+ ` : existing;
12228
+ fs7.writeFileSync(file, `${prefix}${missing.join("\n")}
12229
+ `, "utf-8");
12230
+ return true;
12231
+ }
12232
+ function readSyncState(dotDir) {
12233
+ try {
12234
+ const parsed = JSON.parse(fs7.readFileSync(path7.join(dotDir, SYNC_STATE_FILE), "utf-8"));
12235
+ return typeof parsed?.at === "string" ? parsed : null;
12236
+ } catch {
12237
+ return null;
12238
+ }
12239
+ }
12240
+ function writeSyncState(dotDir, state) {
12241
+ try {
12242
+ fs7.mkdirSync(path7.join(dotDir, "state"), { recursive: true });
12243
+ fs7.writeFileSync(path7.join(dotDir, SYNC_STATE_FILE), `${JSON.stringify(state, null, 2)}
12244
+ `, "utf-8");
12245
+ } catch {
12246
+ }
12247
+ }
12248
+ function firstLine(text) {
12249
+ return text.split("\n").map((l) => l.trim()).find(Boolean) ?? "";
12250
+ }
12251
+ function isMissingRemoteRef(stderr) {
12252
+ return /couldn't find remote ref|Remote branch .* not found|invalid refspec|no such ref/i.test(stderr);
12253
+ }
12254
+ function commitCount(dotDir, range) {
12255
+ const r = git(["rev-list", "--count", range], dotDir);
12256
+ return r.ok ? Number.parseInt(r.stdout, 10) || 0 : 0;
12257
+ }
12258
+ function identity(dotDir, agent) {
12259
+ const args = ["-c", "commit.gpgsign=false"];
12260
+ if (!git(["config", "--get", "user.email"], dotDir).ok) {
12261
+ args.push("-c", "user.name=brainfile", "-c", "user.email=brainfile@localhost");
12262
+ }
12263
+ const env = {};
12264
+ const name = agent?.replace(/[\r\n<>]/g, "").trim();
12265
+ if (name) {
12266
+ env.GIT_AUTHOR_NAME = name;
12267
+ env.GIT_AUTHOR_EMAIL = `${name}@brainfile.local`;
12268
+ }
12269
+ return { args, env };
12270
+ }
12271
+ function resolveCompleteVersusEdit(dotDir) {
12272
+ const status = git(["status", "--porcelain", "-z"], dotDir);
12273
+ if (!status.ok) return [];
12274
+ const resolved = [];
12275
+ for (const entry of status.stdout.split("\0")) {
12276
+ if (entry.length < 4) continue;
12277
+ const xy = entry.slice(0, 2);
12278
+ const file = entry.slice(3);
12279
+ if (xy !== "DU" && xy !== "UD") continue;
12280
+ if (!file.startsWith("board/")) continue;
12281
+ const editedStage = xy === "DU" ? 3 : 2;
12282
+ const base = git(["show", `:1:${file}`], dotDir);
12283
+ const edited = git(["show", `:${editedStage}:${file}`], dotDir);
12284
+ const archived = path7.join(dotDir, "logs", path7.basename(file));
12285
+ if (edited.ok && fs7.existsSync(archived)) {
12286
+ const summary = summarizeFieldChanges(base.ok ? base.stdout : "", edited.stdout);
12287
+ const entryLine = `- ${(/* @__PURE__ */ new Date()).toISOString()}: [sync] edited elsewhere after completion${summary ? ` (${summary})` : ""}`;
12288
+ let content = fs7.readFileSync(archived, "utf-8");
12289
+ if (/^## Log\s*$/m.test(content)) {
12290
+ content = `${content.replace(/\s+$/, "")}
12291
+ ${entryLine}
12292
+ `;
12293
+ } else {
12294
+ content = `${content.replace(/\s+$/, "")}
12295
+
12296
+ ## Log
12297
+ ${entryLine}
12298
+ `;
12299
+ }
12300
+ fs7.writeFileSync(archived, content, "utf-8");
12301
+ git(["add", "--", path7.relative(dotDir, archived)], dotDir);
12302
+ }
12303
+ if (git(["rm", "--quiet", "--cached", "--", file], dotDir).ok) {
12304
+ try {
12305
+ fs7.rmSync(path7.join(dotDir, file), { force: true });
12306
+ } catch {
12307
+ }
12308
+ resolved.push(file);
12309
+ }
12310
+ }
12311
+ return resolved;
12312
+ }
12313
+ function syncBoard(dotDir, options = {}) {
12314
+ const pull = options.pull ?? true;
12315
+ const push = options.push ?? true;
12316
+ const result = { ok: true, pulled: 0, pushed: 0, conflicts: [] };
12317
+ if (!isTrackedBoard(dotDir)) return { ...result, skipped: "not-tracked" };
12318
+ const remote = boardRemote(dotDir);
12319
+ if (!remote) return { ...result, skipped: "no-remote" };
12320
+ const remoteBranch = boardRemoteBranch(dotDir);
12321
+ result.remote = remote;
12322
+ result.remoteBranch = remoteBranch;
12323
+ const stuck = unmergedFiles(dotDir);
12324
+ if (stuck.length > 0) {
12325
+ return finish(dotDir, { ...result, ok: false, failure: "conflict", conflicts: stuck, warning: `unresolved conflicts: ${stuck.join(", ")}` });
12326
+ }
12327
+ commitHandEdits(dotDir);
12328
+ if (ensureBoardAttributes(dotDir)) commitBoard(dotDir, { message: "chore: merge attributes" });
12329
+ registerMergeDriver(dotDir);
12330
+ let fetched = false;
12331
+ if (pull) {
12332
+ const fetch2 = git(["fetch", "--quiet", remote, remoteBranch], dotDir);
12333
+ if (fetch2.ok) {
12334
+ fetched = true;
12335
+ } else if (!isMissingRemoteRef(fetch2.stderr)) {
12336
+ return finish(dotDir, { ...result, ok: false, failure: "offline", warning: `fetch from ${remote} failed: ${firstLine(fetch2.stderr)}` });
12337
+ }
12338
+ if (fetched && !git(["merge-base", "--is-ancestor", "FETCH_HEAD", "HEAD"], dotDir).ok) {
12339
+ const before = git(["rev-parse", "HEAD"], dotDir).stdout;
12340
+ const id = identity(dotDir, options.agent);
12341
+ const merge = git([...id.args, "merge", "--quiet", "--no-edit", "FETCH_HEAD"], dotDir, id.env);
12342
+ if (!merge.ok) {
12343
+ resolveCompleteVersusEdit(dotDir);
12344
+ const remaining = unmergedFiles(dotDir);
12345
+ if (remaining.length > 0) {
12346
+ return finish(dotDir, {
12347
+ ...result,
12348
+ ok: false,
12349
+ failure: "conflict",
12350
+ conflicts: remaining,
12351
+ warning: `merge left conflicts in ${remaining.join(", ")} \u2014 resolve them in ${dotDir}, then run sync again`
12352
+ });
12353
+ }
12354
+ const commit = git([...id.args, "commit", "--quiet", "--no-edit", "--no-verify"], dotDir, id.env);
12355
+ if (!commit.ok) {
12356
+ return finish(dotDir, { ...result, ok: false, failure: "error", warning: `merge commit failed: ${firstLine(commit.stderr)}` });
12357
+ }
12358
+ }
12359
+ result.pulled = commitCount(dotDir, `${before}..FETCH_HEAD`);
12360
+ }
12361
+ }
12362
+ if (push) {
12363
+ const ahead = fetched ? commitCount(dotDir, "FETCH_HEAD..HEAD") : pendingChanges(dotDir);
12364
+ if (ahead > 0) {
12365
+ const pushed = git(["push", "--quiet", remote, `HEAD:refs/heads/${remoteBranch}`], dotDir);
12366
+ if (!pushed.ok) {
12367
+ const rejected = /rejected|non-fast-forward|fetch first/i.test(pushed.stderr);
12368
+ return finish(dotDir, {
12369
+ ...result,
12370
+ ok: false,
12371
+ failure: rejected ? "rejected" : "offline",
12372
+ warning: rejected ? `push to ${remote} rejected (the remote moved) \u2014 run sync again` : `push to ${remote} failed: ${firstLine(pushed.stderr)}`
12373
+ });
12374
+ }
12375
+ result.pushed = ahead;
12376
+ }
12377
+ }
12378
+ return finish(dotDir, result);
12379
+ }
12380
+ function finish(dotDir, result) {
12381
+ writeSyncState(dotDir, {
12382
+ at: (/* @__PURE__ */ new Date()).toISOString(),
12383
+ remote: result.remote ?? "",
12384
+ remoteBranch: result.remoteBranch ?? "",
12385
+ ok: result.ok,
12386
+ ...result.warning ? { warning: result.warning } : {}
12387
+ });
12388
+ return result;
12389
+ }
12390
+ function secondsSinceSync(dotDir, now = Date.now()) {
12391
+ const state = readSyncState(dotDir);
12392
+ if (!state) return null;
12393
+ const at = Date.parse(state.at);
12394
+ return Number.isNaN(at) ? null : Math.max(0, Math.round((now - at) / 1e3));
12395
+ }
12396
+ function syncStatusLabel(dotDir, now = Date.now()) {
12397
+ if (!isTrackedBoard(dotDir)) return null;
12398
+ if (!boardRemote(dotDir)) return "local only";
12399
+ const state = readSyncState(dotDir);
12400
+ if (!state) return "not synced yet";
12401
+ if (!state.ok) return "not synced \xB7 saved locally";
12402
+ const seconds = secondsSinceSync(dotDir, now) ?? 0;
12403
+ const age = seconds < 60 ? `${seconds}s` : seconds < 3600 ? `${Math.round(seconds / 60)}m` : `${Math.round(seconds / 3600)}h`;
12404
+ return `synced ${age} ago`;
12405
+ }
12406
+ function pendingChanges(dotDir) {
12407
+ const remote = boardRemote(dotDir);
12408
+ if (!remote) return 0;
12409
+ const tracking = `refs/remotes/${remote}/${boardRemoteBranch(dotDir)}`;
12410
+ const known = git(["rev-parse", "--verify", "--quiet", tracking], dotDir).ok;
12411
+ return commitCount(dotDir, known ? `${tracking}..HEAD` : "HEAD");
12412
+ }
12413
+ var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
12414
+ function syncMessage(result) {
12415
+ if (result.skipped === "not-tracked") {
12416
+ return { tone: "warn", text: "This board is plain files on this machine and is not shared. To share it: brainfile migrate --to-branch" };
12417
+ }
12418
+ if (result.skipped === "no-remote") {
12419
+ return { tone: "warn", text: "This board is only on this machine. To share it: brainfile sync --set-remote origin" };
12420
+ }
12421
+ const remote = result.remote ?? "the remote";
12422
+ if (!result.ok) {
12423
+ switch (result.failure) {
12424
+ case "conflict":
12425
+ return {
12426
+ tone: "warn",
12427
+ 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.`
12428
+ };
12429
+ case "rejected":
12430
+ return { tone: "warn", text: `Someone shared changes through ${remote} at the same moment. Run brainfile sync again to combine them. Nothing was lost.` };
12431
+ case "offline":
12432
+ return {
12433
+ tone: "warn",
12434
+ text: `Couldn't reach ${remote}. Nothing was lost: your changes are saved on this machine and will be sent on the next sync.`,
12435
+ detail: result.warning
12436
+ };
12437
+ default:
12438
+ return { tone: "warn", text: `Sync with ${remote} stopped. Your changes are saved on this machine.`, detail: result.warning };
12439
+ }
12440
+ }
12441
+ const parts = [];
12442
+ if (result.pulled > 0) parts.push(`received ${plural(result.pulled, "change")}`);
12443
+ if (result.pushed > 0) parts.push(`sent ${plural(result.pushed, "change")}`);
12444
+ return { tone: "ok", text: parts.length ? `Shared through ${remote}: ${parts.join(", ")}.` : `Up to date with ${remote}.` };
12445
+ }
12446
+
12447
+ // src/utils/board-autosync.ts
12448
+ import * as fs8 from "fs";
12449
+ import * as path8 from "path";
12450
+ import { spawn } from "child_process";
12451
+ var AUTOSYNC_DELAY_MS = 5e3;
12452
+ var FETCH_MAX_AGE_SECONDS = 60;
12453
+ var LOCK_FILE = path8.join("state", "autosync.lock");
12454
+ var LOCK_STALE_MS = 6e4;
12455
+ function effectiveAutosync(dotDir) {
12456
+ const override = process.env.BRAINFILE_AUTOSYNC;
12457
+ if (override === "off" || override === "push" || override === "full") return override;
12458
+ return boardAutosync(dotDir);
12459
+ }
12460
+ function autosyncCliPath() {
12461
+ const candidates = [
12462
+ path8.join(__dirname, "cli.js"),
12463
+ path8.join(__dirname, "..", "cli.js"),
12464
+ path8.join(__dirname, "..", "..", "dist", "cli.js")
12465
+ ];
12466
+ return candidates.find((p) => fs8.existsSync(p)) ?? null;
12467
+ }
12468
+ function pidAlive(pid) {
12469
+ try {
12470
+ process.kill(pid, 0);
12471
+ return true;
12472
+ } catch {
12473
+ return false;
12474
+ }
12475
+ }
12476
+ function pushPending(dotDir) {
12477
+ const lock = path8.join(dotDir, LOCK_FILE);
12478
+ try {
12479
+ const st = fs8.statSync(lock);
12480
+ if (Date.now() - st.mtimeMs > LOCK_STALE_MS) return false;
12481
+ const pid = Number.parseInt(fs8.readFileSync(lock, "utf-8"), 10);
12482
+ return Number.isFinite(pid) && pidAlive(pid);
12483
+ } catch {
12484
+ return false;
12485
+ }
12486
+ }
12487
+ function clearPushLock(dotDir) {
12488
+ try {
12489
+ fs8.rmSync(path8.join(dotDir, LOCK_FILE), { force: true });
12490
+ } catch {
12491
+ }
12492
+ }
12493
+ function schedulePush(dotDir, delayMs = AUTOSYNC_DELAY_MS) {
12494
+ if (!isTrackedBoard(dotDir)) return false;
12495
+ const mode = effectiveAutosync(dotDir);
12496
+ if (mode === "off" || !boardRemote(dotDir)) return false;
12497
+ if (pushPending(dotDir)) return false;
12498
+ const cli = autosyncCliPath();
12499
+ if (!cli) return false;
12500
+ const delay = process.env.BRAINFILE_AUTOSYNC_DELAY_MS ? Number(process.env.BRAINFILE_AUTOSYNC_DELAY_MS) : delayMs;
12501
+ try {
12502
+ const child = spawn(
12503
+ process.execPath,
12504
+ [cli, "sync", "--push", "--file", path8.join(dotDir, "brainfile.md"), "--wait", String(delay)],
12505
+ { cwd: dotDir, detached: true, stdio: "ignore", env: { ...process.env, BRAINFILE_AUTOSYNC: "off" } }
12506
+ );
12507
+ child.unref();
12508
+ if (child.pid) {
12509
+ fs8.mkdirSync(path8.join(dotDir, "state"), { recursive: true });
12510
+ fs8.writeFileSync(path8.join(dotDir, LOCK_FILE), String(child.pid), "utf-8");
12511
+ }
12512
+ return true;
12513
+ } catch {
12514
+ return false;
12515
+ }
12516
+ }
12517
+ function fetchBeforeRead(dotDir, maxAgeSeconds = FETCH_MAX_AGE_SECONDS) {
12518
+ if (!isTrackedBoard(dotDir)) return null;
12519
+ if (effectiveAutosync(dotDir) !== "full") return null;
12520
+ const age = secondsSinceSync(dotDir);
12521
+ if (age !== null && age < maxAgeSeconds) return null;
12522
+ return syncBoard(dotDir, { pull: true, push: false });
12523
+ }
12524
+ var installed = false;
12525
+ function installAutosync() {
12526
+ if (installed) return;
12527
+ installed = true;
12528
+ onBoardCommitted((dotDir) => {
12529
+ schedulePush(dotDir);
12530
+ });
11656
12531
  }
11657
12532
 
11658
12533
  // src/utils/config.ts
11659
- import * as fs5 from "fs";
11660
- import * as path5 from "path";
11661
- import * as os from "os";
12534
+ import * as fs9 from "fs";
12535
+ import * as path9 from "path";
12536
+ import * as os4 from "os";
11662
12537
  function resolveHomeDir() {
11663
12538
  const envHome = process.env.XDG_CONFIG_HOME?.trim();
11664
12539
  if (envHome) {
11665
12540
  return envHome;
11666
12541
  }
11667
12542
  const home = process.env.HOME?.trim();
11668
- return home || os.homedir();
12543
+ return home || os4.homedir();
11669
12544
  }
11670
12545
  function getConfigDir() {
11671
12546
  const xdgConfigHome = process.env.XDG_CONFIG_HOME?.trim();
11672
12547
  if (xdgConfigHome) {
11673
- return path5.join(xdgConfigHome, "brainfile");
12548
+ return path9.join(xdgConfigHome, "brainfile");
11674
12549
  }
11675
- return path5.join(resolveHomeDir(), ".config", "brainfile");
12550
+ return path9.join(resolveHomeDir(), ".config", "brainfile");
11676
12551
  }
11677
12552
  function getConfigPath() {
11678
- return path5.join(getConfigDir(), "config.json");
12553
+ return path9.join(getConfigDir(), "config.json");
11679
12554
  }
11680
12555
  function ensureConfigDir() {
11681
12556
  const configDir = getConfigDir();
11682
- if (!fs5.existsSync(configDir)) {
11683
- fs5.mkdirSync(configDir, { recursive: true, mode: 448 });
12557
+ if (!fs9.existsSync(configDir)) {
12558
+ fs9.mkdirSync(configDir, { recursive: true, mode: 448 });
11684
12559
  }
11685
12560
  }
11686
12561
  function loadConfig() {
11687
12562
  const configFile = getConfigPath();
11688
12563
  try {
11689
- if (fs5.existsSync(configFile)) {
11690
- const content = fs5.readFileSync(configFile, "utf-8");
12564
+ if (fs9.existsSync(configFile)) {
12565
+ const content = fs9.readFileSync(configFile, "utf-8");
11691
12566
  return JSON.parse(content);
11692
12567
  }
11693
12568
  } catch (error) {
@@ -11698,7 +12573,7 @@ function loadConfig() {
11698
12573
  function saveConfig(config) {
11699
12574
  const configFile = getConfigPath();
11700
12575
  ensureConfigDir();
11701
- fs5.writeFileSync(configFile, JSON.stringify(config, null, 2), {
12576
+ fs9.writeFileSync(configFile, JSON.stringify(config, null, 2), {
11702
12577
  encoding: "utf-8",
11703
12578
  mode: 384
11704
12579
  // Owner read/write only
@@ -11773,11 +12648,11 @@ function isValidDestination(dest) {
11773
12648
  }
11774
12649
 
11775
12650
  // src/utils/auth-store.ts
11776
- import * as fs6 from "fs";
11777
- import * as path6 from "path";
12651
+ import * as fs10 from "fs";
12652
+ import * as path10 from "path";
11778
12653
  import { execSync } from "child_process";
11779
12654
  var SERVICE_NAME = "brainfile";
11780
- var AUTH_FILE = path6.join(getConfigDir(), "auth.json");
12655
+ var AUTH_FILE = path10.join(getConfigDir(), "auth.json");
11781
12656
  var ENV_VAR_MAP = {
11782
12657
  github: ["GITHUB_TOKEN", "GH_TOKEN"],
11783
12658
  linear: ["LINEAR_API_KEY", "LINEAR_TOKEN"]
@@ -11947,8 +12822,8 @@ var AuthStore = class {
11947
12822
  }
11948
12823
  loadAuthFile() {
11949
12824
  try {
11950
- if (fs6.existsSync(AUTH_FILE)) {
11951
- const content = fs6.readFileSync(AUTH_FILE, "utf-8");
12825
+ if (fs10.existsSync(AUTH_FILE)) {
12826
+ const content = fs10.readFileSync(AUTH_FILE, "utf-8");
11952
12827
  return JSON.parse(content);
11953
12828
  }
11954
12829
  } catch {
@@ -11957,7 +12832,7 @@ var AuthStore = class {
11957
12832
  }
11958
12833
  saveAuthFile(data) {
11959
12834
  ensureConfigDir();
11960
- fs6.writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), {
12835
+ fs10.writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), {
11961
12836
  encoding: "utf-8",
11962
12837
  mode: 384
11963
12838
  // Owner read/write only
@@ -12205,7 +13080,7 @@ async function logoutGitHub() {
12205
13080
  await authStore.clear("github");
12206
13081
  }
12207
13082
  function sleep(ms) {
12208
- return new Promise((resolve5) => setTimeout(resolve5, ms));
13083
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
12209
13084
  }
12210
13085
 
12211
13086
  // src/utils/linear-auth.ts
@@ -12552,12 +13427,51 @@ export {
12552
13427
  renderCliError,
12553
13428
  V1_UNSUPPORTED_MESSAGE,
12554
13429
  assertV2Brainfile,
13430
+ git,
13431
+ gitToplevel,
13432
+ listWorktrees,
13433
+ boardBranch,
13434
+ boardRepoKind,
13435
+ isTrackedBoard,
13436
+ findTrackedBoardDir,
13437
+ materializeBoardWorktree,
13438
+ ensureExcludeEntry,
13439
+ removeExcludeEntry,
13440
+ createOrphanBoardWorktree,
13441
+ initStandaloneBoardRepo,
13442
+ boardIsDirty,
13443
+ commitBoard,
13444
+ commitHandEdits,
13445
+ resolveAgentName,
13446
+ homeBoardDir,
13447
+ isGlobalBoardRequested,
13448
+ describeCommandForCommit,
13449
+ scheduleBoardCommit,
12555
13450
  resolveCliBrainfilePath,
13451
+ tryResolveBoardDir,
12556
13452
  workspaceRootFromBrainfilePath,
12557
13453
  probeWorkspaceFormat,
12558
13454
  shouldSuggestV2Migration,
12559
13455
  markV2MigrationHintShown,
12560
13456
  import_core5 as import_core,
13457
+ mergeDriverCommand,
13458
+ boardRemote,
13459
+ boardRemoteBranch,
13460
+ boardAutosync,
13461
+ setBoardAutosync,
13462
+ setBoardRemote,
13463
+ registerMergeDriver,
13464
+ ensureBoardAttributes,
13465
+ readSyncState,
13466
+ syncBoard,
13467
+ secondsSinceSync,
13468
+ syncStatusLabel,
13469
+ pendingChanges,
13470
+ syncMessage,
13471
+ effectiveAutosync,
13472
+ clearPushLock,
13473
+ fetchBeforeRead,
13474
+ installAutosync,
12561
13475
  getConfigPath,
12562
13476
  loadConfig,
12563
13477
  saveConfig,