@brainfile/cli 0.13.3 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +129 -354
  3. package/dist/cli.js +91 -5
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/add.d.ts +3 -0
  6. package/dist/commands/add.d.ts.map +1 -1
  7. package/dist/commands/add.js +163 -3
  8. package/dist/commands/add.js.map +1 -1
  9. package/dist/commands/adr.d.ts +22 -0
  10. package/dist/commands/adr.d.ts.map +1 -0
  11. package/dist/commands/adr.js +182 -0
  12. package/dist/commands/adr.js.map +1 -0
  13. package/dist/commands/archive.d.ts.map +1 -1
  14. package/dist/commands/archive.js +147 -0
  15. package/dist/commands/archive.js.map +1 -1
  16. package/dist/commands/complete.d.ts +30 -0
  17. package/dist/commands/complete.d.ts.map +1 -0
  18. package/dist/commands/complete.js +254 -0
  19. package/dist/commands/complete.js.map +1 -0
  20. package/dist/commands/contract.d.ts.map +1 -1
  21. package/dist/commands/contract.js +2 -0
  22. package/dist/commands/contract.js.map +1 -1
  23. package/dist/commands/delete.d.ts.map +1 -1
  24. package/dist/commands/delete.js +29 -1
  25. package/dist/commands/delete.js.map +1 -1
  26. package/dist/commands/init.d.ts.map +1 -1
  27. package/dist/commands/init.js +38 -19
  28. package/dist/commands/init.js.map +1 -1
  29. package/dist/commands/list.d.ts +1 -0
  30. package/dist/commands/list.d.ts.map +1 -1
  31. package/dist/commands/list.js +35 -12
  32. package/dist/commands/list.js.map +1 -1
  33. package/dist/commands/log.d.ts +52 -0
  34. package/dist/commands/log.d.ts.map +1 -0
  35. package/dist/commands/log.js +246 -0
  36. package/dist/commands/log.js.map +1 -0
  37. package/dist/commands/mcp.d.ts.map +1 -1
  38. package/dist/commands/mcp.js +864 -44
  39. package/dist/commands/mcp.js.map +1 -1
  40. package/dist/commands/migrate.d.ts +4 -3
  41. package/dist/commands/migrate.d.ts.map +1 -1
  42. package/dist/commands/migrate.js +225 -33
  43. package/dist/commands/migrate.js.map +1 -1
  44. package/dist/commands/move.d.ts.map +1 -1
  45. package/dist/commands/move.js +90 -0
  46. package/dist/commands/move.js.map +1 -1
  47. package/dist/commands/patch.d.ts.map +1 -1
  48. package/dist/commands/patch.js +85 -13
  49. package/dist/commands/patch.js.map +1 -1
  50. package/dist/commands/search.d.ts +33 -0
  51. package/dist/commands/search.d.ts.map +1 -0
  52. package/dist/commands/search.js +209 -0
  53. package/dist/commands/search.js.map +1 -0
  54. package/dist/commands/show.d.ts.map +1 -1
  55. package/dist/commands/show.js +75 -1
  56. package/dist/commands/show.js.map +1 -1
  57. package/dist/commands/subtask.d.ts.map +1 -1
  58. package/dist/commands/subtask.js +72 -5
  59. package/dist/commands/subtask.js.map +1 -1
  60. package/dist/commands/tui.d.ts.map +1 -1
  61. package/dist/commands/tui.js +10 -0
  62. package/dist/commands/tui.js.map +1 -1
  63. package/dist/commands/types.d.ts +40 -0
  64. package/dist/commands/types.d.ts.map +1 -0
  65. package/dist/commands/types.js +242 -0
  66. package/dist/commands/types.js.map +1 -0
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +3 -1
  70. package/dist/index.js.map +1 -1
  71. package/dist/lib/contractRunner.d.ts.map +1 -1
  72. package/dist/lib/contractRunner.js +177 -12
  73. package/dist/lib/contractRunner.js.map +1 -1
  74. package/dist/schemas/board.json +105 -18
  75. package/dist/tui/BrainfileTUI.d.ts.map +1 -1
  76. package/dist/tui/BrainfileTUI.js +23 -20
  77. package/dist/tui/BrainfileTUI.js.map +1 -1
  78. package/dist/tui/actions.d.ts +5 -5
  79. package/dist/tui/actions.d.ts.map +1 -1
  80. package/dist/tui/actions.js +335 -47
  81. package/dist/tui/actions.js.map +1 -1
  82. package/dist/tui/components/ArchivePanel.js +1 -1
  83. package/dist/tui/components/ArchivePanel.js.map +1 -1
  84. package/dist/tui/components/ColumnTabs.d.ts.map +1 -1
  85. package/dist/tui/components/ColumnTabs.js +6 -2
  86. package/dist/tui/components/ColumnTabs.js.map +1 -1
  87. package/dist/tui/components/HelpOverlay.js +3 -3
  88. package/dist/tui/components/HelpOverlay.js.map +1 -1
  89. package/dist/tui/components/LogsPanel.d.ts +16 -0
  90. package/dist/tui/components/LogsPanel.d.ts.map +1 -0
  91. package/dist/tui/components/LogsPanel.js +115 -0
  92. package/dist/tui/components/LogsPanel.js.map +1 -0
  93. package/dist/tui/components/MainPanelTabs.d.ts +2 -2
  94. package/dist/tui/components/MainPanelTabs.d.ts.map +1 -1
  95. package/dist/tui/components/MainPanelTabs.js +3 -3
  96. package/dist/tui/components/MainPanelTabs.js.map +1 -1
  97. package/dist/tui/components/StackedTaskList.d.ts +2 -1
  98. package/dist/tui/components/StackedTaskList.d.ts.map +1 -1
  99. package/dist/tui/components/StackedTaskList.js +9 -9
  100. package/dist/tui/components/StackedTaskList.js.map +1 -1
  101. package/dist/tui/components/TaskCard.d.ts +2 -1
  102. package/dist/tui/components/TaskCard.d.ts.map +1 -1
  103. package/dist/tui/components/TaskCard.js +41 -5
  104. package/dist/tui/components/TaskCard.js.map +1 -1
  105. package/dist/tui/components/TaskCardMeasure.d.ts +2 -16
  106. package/dist/tui/components/TaskCardMeasure.d.ts.map +1 -1
  107. package/dist/tui/components/TaskCardMeasure.js +30 -25
  108. package/dist/tui/components/TaskCardMeasure.js.map +1 -1
  109. package/dist/tui/components/TaskDetail.d.ts +2 -3
  110. package/dist/tui/components/TaskDetail.d.ts.map +1 -1
  111. package/dist/tui/components/TaskDetail.js +35 -12
  112. package/dist/tui/components/TaskDetail.js.map +1 -1
  113. package/dist/tui/components/TaskList.d.ts +2 -1
  114. package/dist/tui/components/TaskList.d.ts.map +1 -1
  115. package/dist/tui/components/TaskList.js +5 -5
  116. package/dist/tui/components/TaskList.js.map +1 -1
  117. package/dist/tui/components/index.d.ts +2 -2
  118. package/dist/tui/components/index.d.ts.map +1 -1
  119. package/dist/tui/components/index.js +3 -3
  120. package/dist/tui/components/index.js.map +1 -1
  121. package/dist/tui/hooks/useBrainfileLoader.d.ts.map +1 -1
  122. package/dist/tui/hooks/useBrainfileLoader.js +97 -31
  123. package/dist/tui/hooks/useBrainfileLoader.js.map +1 -1
  124. package/dist/tui/hooks/useKeyboardNavigation.d.ts.map +1 -1
  125. package/dist/tui/hooks/useKeyboardNavigation.js +47 -47
  126. package/dist/tui/hooks/useKeyboardNavigation.js.map +1 -1
  127. package/dist/tui/types.d.ts +7 -7
  128. package/dist/tui/types.d.ts.map +1 -1
  129. package/dist/utils/board-types.d.ts +13 -0
  130. package/dist/utils/board-types.d.ts.map +1 -0
  131. package/dist/utils/board-types.js +7 -0
  132. package/dist/utils/board-types.js.map +1 -0
  133. package/dist/utils/dot-brainfile.d.ts +9 -0
  134. package/dist/utils/dot-brainfile.d.ts.map +1 -0
  135. package/dist/utils/dot-brainfile.js +74 -0
  136. package/dist/utils/dot-brainfile.js.map +1 -0
  137. package/dist/utils/strict-validation.d.ts +8 -0
  138. package/dist/utils/strict-validation.d.ts.map +1 -0
  139. package/dist/utils/strict-validation.js +41 -0
  140. package/dist/utils/strict-validation.js.map +1 -0
  141. package/dist/utils/v2-detect.d.ts +28 -0
  142. package/dist/utils/v2-detect.d.ts.map +1 -0
  143. package/dist/utils/v2-detect.js +112 -0
  144. package/dist/utils/v2-detect.js.map +1 -0
  145. package/dist/utils/v2-tasks.d.ts +121 -0
  146. package/dist/utils/v2-tasks.d.ts.map +1 -0
  147. package/dist/utils/v2-tasks.js +384 -0
  148. package/dist/utils/v2-tasks.js.map +1 -0
  149. package/dist/utils/workspace-format.d.ts +25 -0
  150. package/dist/utils/workspace-format.d.ts.map +1 -0
  151. package/dist/utils/workspace-format.js +106 -0
  152. package/dist/utils/workspace-format.js.map +1 -0
  153. package/package.json +3 -3
  154. package/state.json +3 -0
@@ -47,7 +47,7 @@ exports.copyToClipboard = copyToClipboard;
47
47
  exports.addRuleAction = addRuleAction;
48
48
  exports.updateRuleAction = updateRuleAction;
49
49
  exports.deleteRuleAction = deleteRuleAction;
50
- exports.loadArchive = loadArchive;
50
+ exports.loadLogs = loadLogs;
51
51
  exports.restoreTaskAction = restoreTaskAction;
52
52
  exports.deleteArchivedTaskAction = deleteArchivedTaskAction;
53
53
  /**
@@ -62,6 +62,7 @@ const os = __importStar(require("os"));
62
62
  const child_process_1 = require("child_process");
63
63
  const core_1 = require("@brainfile/core");
64
64
  const config_1 = require("../utils/config");
65
+ const v2_detect_1 = require("../utils/v2-detect");
65
66
  const github_auth_1 = require("../utils/github-auth");
66
67
  const linear_auth_1 = require("../utils/linear-auth");
67
68
  /**
@@ -93,11 +94,53 @@ function writeBrainfile(filePath, board) {
93
94
  return { success: false, error: err instanceof Error ? err.message : String(err) };
94
95
  }
95
96
  }
97
+ /**
98
+ * Read board with tasks populated (v1: embedded, v2: reconstructed from board/ files).
99
+ */
100
+ function readBoardState(filePath) {
101
+ if ((0, v2_detect_1.isV2)(filePath)) {
102
+ try {
103
+ const content = fs.readFileSync(filePath, 'utf-8');
104
+ const board = (0, v2_detect_1.buildBoardFromV2)(filePath);
105
+ return { board, content };
106
+ }
107
+ catch (err) {
108
+ return { board: null, content: '', error: err instanceof Error ? err.message : String(err) };
109
+ }
110
+ }
111
+ return readBrainfile(filePath);
112
+ }
113
+ /**
114
+ * Read board config only (for rules/settings changes that should not touch task files).
115
+ */
116
+ function readBoardConfigOnly(filePath) {
117
+ if ((0, v2_detect_1.isV2)(filePath)) {
118
+ try {
119
+ const content = fs.readFileSync(filePath, 'utf-8');
120
+ const board = (0, v2_detect_1.readV2BoardConfig)(filePath);
121
+ return { board, content };
122
+ }
123
+ catch (err) {
124
+ return { board: null, content: '', error: err instanceof Error ? err.message : String(err) };
125
+ }
126
+ }
127
+ return readBrainfile(filePath);
128
+ }
129
+ /**
130
+ * Resolve a task file path in v2 board directory.
131
+ */
132
+ function resolveV2TaskPath(filePath, taskId) {
133
+ const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
134
+ const doc = (0, core_1.findTask)(dirs.boardDir, taskId);
135
+ if (!doc || !doc.filePath)
136
+ return null;
137
+ return { taskPath: doc.filePath, task: doc.task };
138
+ }
96
139
  /**
97
140
  * Move a task to a different column
98
141
  */
99
142
  function moveTaskAction(filePath, taskId, targetColumnId) {
100
- const { board, error } = readBrainfile(filePath);
143
+ const { board, error } = readBoardState(filePath);
101
144
  if (!board)
102
145
  return { success: false, error };
103
146
  const taskInfo = (0, core_1.findTaskById)(board, taskId);
@@ -109,6 +152,15 @@ function moveTaskAction(filePath, taskId, targetColumnId) {
109
152
  if (taskInfo.column.id === targetColumn.id) {
110
153
  return { success: true, message: 'Task already in this column' };
111
154
  }
155
+ if ((0, v2_detect_1.isV2)(filePath)) {
156
+ const located = resolveV2TaskPath(filePath, taskId);
157
+ if (!located)
158
+ return { success: false, error: `Task ${taskId} not found` };
159
+ const moveResult = (0, core_1.moveTaskFile)(located.taskPath, targetColumn.id, targetColumn.tasks.length);
160
+ if (!moveResult.success)
161
+ return { success: false, error: moveResult.error };
162
+ return { success: true, message: `Moved to ${targetColumn.title}` };
163
+ }
112
164
  const result = (0, core_1.moveTask)(board, taskId, taskInfo.column.id, targetColumn.id, targetColumn.tasks.length);
113
165
  if (!result.success)
114
166
  return { success: false, error: result.error };
@@ -121,12 +173,21 @@ function moveTaskAction(filePath, taskId, targetColumnId) {
121
173
  * Delete a task permanently
122
174
  */
123
175
  function deleteTaskAction(filePath, taskId) {
124
- const { board, error } = readBrainfile(filePath);
176
+ const { board, error } = readBoardState(filePath);
125
177
  if (!board)
126
178
  return { success: false, error };
127
179
  const taskInfo = (0, core_1.findTaskById)(board, taskId);
128
180
  if (!taskInfo)
129
181
  return { success: false, error: `Task ${taskId} not found` };
182
+ if ((0, v2_detect_1.isV2)(filePath)) {
183
+ const located = resolveV2TaskPath(filePath, taskId);
184
+ if (!located)
185
+ return { success: false, error: `Task ${taskId} not found` };
186
+ const result = (0, core_1.deleteTaskFile)(located.taskPath);
187
+ if (!result.success)
188
+ return { success: false, error: result.error };
189
+ return { success: true, message: `Deleted ${taskId}` };
190
+ }
130
191
  const result = (0, core_1.deleteTask)(board, taskInfo.column.id, taskId);
131
192
  if (!result.success)
132
193
  return { success: false, error: result.error };
@@ -150,7 +211,7 @@ function getArchivePath(filePath) {
150
211
  */
151
212
  function createEmptyArchiveBoard() {
152
213
  return {
153
- title: 'Archive',
214
+ title: 'Logs',
154
215
  columns: [],
155
216
  archive: [],
156
217
  };
@@ -160,6 +221,16 @@ function createEmptyArchiveBoard() {
160
221
  * Per protocol spec, archived tasks go to a separate file, not inline archive array
161
222
  */
162
223
  function archiveTaskAction(filePath, taskId) {
224
+ if ((0, v2_detect_1.isV2)(filePath)) {
225
+ const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
226
+ const located = resolveV2TaskPath(filePath, taskId);
227
+ if (!located)
228
+ return { success: false, error: `Task ${taskId} not found` };
229
+ const result = (0, core_1.completeTaskFile)(located.taskPath, dirs.logsDir);
230
+ if (!result.success)
231
+ return { success: false, error: result.error };
232
+ return { success: true, message: `Moved ${taskId} to logs` };
233
+ }
163
234
  const { board, error } = readBrainfile(filePath);
164
235
  if (!board)
165
236
  return { success: false, error };
@@ -213,17 +284,17 @@ function archiveTaskAction(filePath, taskId) {
213
284
  // This is a partial failure state - log and report
214
285
  return {
215
286
  success: false,
216
- error: `Task removed from board but failed to write archive: ${err instanceof Error ? err.message : String(err)}`,
287
+ error: `Task removed from board but failed to write logs: ${err instanceof Error ? err.message : String(err)}`,
217
288
  };
218
289
  }
219
- return { success: true, message: `Archived ${taskId}` };
290
+ return { success: true, message: `Moved ${taskId} to logs` };
220
291
  }
221
292
  /**
222
293
  * Archive a task with support for external destinations (GitHub, Linear)
223
294
  * Respects archive.destination from brainfile.md or global config
224
295
  */
225
296
  async function archiveTaskActionAsync(filePath, taskId) {
226
- const { board, error } = readBrainfile(filePath);
297
+ const { board, error } = readBoardState(filePath);
227
298
  if (!board)
228
299
  return { success: false, error };
229
300
  const taskInfo = (0, core_1.findTaskById)(board, taskId);
@@ -271,11 +342,19 @@ async function archiveTaskActionAsync(filePath, taskId) {
271
342
  return { success: false, error: `GitHub: ${ghResult.error}` };
272
343
  }
273
344
  // Remove task from board
274
- const deleteResult = (0, core_1.deleteTask)(board, columnId, taskId);
275
- if (deleteResult.success) {
276
- writeBrainfile(filePath, deleteResult.board);
345
+ if ((0, v2_detect_1.isV2)(filePath)) {
346
+ const located = resolveV2TaskPath(filePath, taskId);
347
+ if (located) {
348
+ (0, core_1.deleteTaskFile)(located.taskPath);
349
+ }
277
350
  }
278
- return { success: true, message: `Archived to GitHub #${ghResult.issueNumber}` };
351
+ else {
352
+ const deleteResult = (0, core_1.deleteTask)(board, columnId, taskId);
353
+ if (deleteResult.success) {
354
+ writeBrainfile(filePath, deleteResult.board);
355
+ }
356
+ }
357
+ return { success: true, message: `Moved to logs via GitHub #${ghResult.issueNumber}` };
279
358
  }
280
359
  // Handle Linear archive
281
360
  if (destination === 'linear') {
@@ -328,11 +407,19 @@ async function archiveTaskActionAsync(filePath, taskId) {
328
407
  return { success: false, error: `Linear: ${linearResult.error}` };
329
408
  }
330
409
  // Remove task from board
331
- const deleteResult = (0, core_1.deleteTask)(board, columnId, taskId);
332
- if (deleteResult.success) {
333
- writeBrainfile(filePath, deleteResult.board);
410
+ if ((0, v2_detect_1.isV2)(filePath)) {
411
+ const located = resolveV2TaskPath(filePath, taskId);
412
+ if (located) {
413
+ (0, core_1.deleteTaskFile)(located.taskPath);
414
+ }
334
415
  }
335
- return { success: true, message: `Archived to Linear ${linearResult.issueId}` };
416
+ else {
417
+ const deleteResult = (0, core_1.deleteTask)(board, columnId, taskId);
418
+ if (deleteResult.success) {
419
+ writeBrainfile(filePath, deleteResult.board);
420
+ }
421
+ }
422
+ return { success: true, message: `Moved to logs via Linear ${linearResult.issueId}` };
336
423
  }
337
424
  return { success: false, error: `Unknown destination: ${destination}` };
338
425
  }
@@ -340,6 +427,73 @@ async function archiveTaskActionAsync(filePath, taskId) {
340
427
  * Patch/update task fields
341
428
  */
342
429
  function patchTaskAction(filePath, taskId, patch) {
430
+ if ((0, v2_detect_1.isV2)(filePath)) {
431
+ const located = resolveV2TaskPath(filePath, taskId);
432
+ if (!located)
433
+ return { success: false, error: `Task ${taskId} not found` };
434
+ const doc = (0, core_1.readTaskFile)(located.taskPath);
435
+ if (!doc)
436
+ return { success: false, error: `Failed to read task ${taskId}` };
437
+ const updatedTask = { ...doc.task };
438
+ if (patch.title !== undefined)
439
+ updatedTask.title = patch.title;
440
+ if (patch.description !== undefined) {
441
+ if (patch.description === null) {
442
+ delete updatedTask.description;
443
+ }
444
+ else {
445
+ updatedTask.description = patch.description;
446
+ }
447
+ }
448
+ if (patch.priority !== undefined) {
449
+ if (patch.priority === null) {
450
+ delete updatedTask.priority;
451
+ }
452
+ else {
453
+ updatedTask.priority = patch.priority;
454
+ }
455
+ }
456
+ if (patch.tags !== undefined) {
457
+ if (patch.tags === null) {
458
+ delete updatedTask.tags;
459
+ }
460
+ else {
461
+ updatedTask.tags = patch.tags;
462
+ }
463
+ }
464
+ if (patch.assignee !== undefined) {
465
+ if (patch.assignee === null) {
466
+ delete updatedTask.assignee;
467
+ }
468
+ else {
469
+ updatedTask.assignee = patch.assignee;
470
+ }
471
+ }
472
+ if (patch.dueDate !== undefined) {
473
+ if (patch.dueDate === null) {
474
+ delete updatedTask.dueDate;
475
+ }
476
+ else {
477
+ updatedTask.dueDate = patch.dueDate;
478
+ }
479
+ }
480
+ if (patch.relatedFiles !== undefined) {
481
+ if (patch.relatedFiles === null) {
482
+ delete updatedTask.relatedFiles;
483
+ }
484
+ else {
485
+ updatedTask.relatedFiles = patch.relatedFiles;
486
+ }
487
+ }
488
+ updatedTask.updatedAt = new Date().toISOString();
489
+ try {
490
+ (0, core_1.writeTaskFile)(located.taskPath, updatedTask, doc.body);
491
+ return { success: true, message: `Updated ${taskId}` };
492
+ }
493
+ catch (err) {
494
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
495
+ }
496
+ }
343
497
  const { board, error } = readBrainfile(filePath);
344
498
  if (!board)
345
499
  return { success: false, error };
@@ -355,13 +509,39 @@ function patchTaskAction(filePath, taskId, patch) {
355
509
  * Cycle task priority: none -> low -> medium -> high -> critical -> none
356
510
  */
357
511
  function cyclePriorityAction(filePath, taskId) {
512
+ const priorities = [undefined, 'low', 'medium', 'high', 'critical'];
513
+ if ((0, v2_detect_1.isV2)(filePath)) {
514
+ const located = resolveV2TaskPath(filePath, taskId);
515
+ if (!located)
516
+ return { success: false, error: `Task ${taskId} not found` };
517
+ const doc = (0, core_1.readTaskFile)(located.taskPath);
518
+ if (!doc)
519
+ return { success: false, error: `Failed to read task ${taskId}` };
520
+ const currentIndex = priorities.indexOf(doc.task.priority);
521
+ const nextIndex = (currentIndex + 1) % priorities.length;
522
+ const nextPriority = priorities[nextIndex];
523
+ const updatedTask = {
524
+ ...doc.task,
525
+ ...(nextPriority ? { priority: nextPriority } : {}),
526
+ updatedAt: new Date().toISOString(),
527
+ };
528
+ if (!nextPriority) {
529
+ delete updatedTask.priority;
530
+ }
531
+ try {
532
+ (0, core_1.writeTaskFile)(located.taskPath, updatedTask, doc.body);
533
+ return { success: true, message: `Priority: ${nextPriority || 'none'}` };
534
+ }
535
+ catch (err) {
536
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
537
+ }
538
+ }
358
539
  const { board, error } = readBrainfile(filePath);
359
540
  if (!board)
360
541
  return { success: false, error };
361
542
  const taskInfo = (0, core_1.findTaskById)(board, taskId);
362
543
  if (!taskInfo)
363
544
  return { success: false, error: `Task ${taskId} not found` };
364
- const priorities = [undefined, 'low', 'medium', 'high', 'critical'];
365
545
  const currentIndex = priorities.indexOf(taskInfo.task.priority);
366
546
  const nextIndex = (currentIndex + 1) % priorities.length;
367
547
  const nextPriority = priorities[nextIndex];
@@ -378,6 +558,37 @@ function cyclePriorityAction(filePath, taskId) {
378
558
  * Toggle a subtask's completion status
379
559
  */
380
560
  function toggleSubtaskAction(filePath, taskId, subtaskId) {
561
+ if ((0, v2_detect_1.isV2)(filePath)) {
562
+ const located = resolveV2TaskPath(filePath, taskId);
563
+ if (!located)
564
+ return { success: false, error: `Task ${taskId} not found` };
565
+ const doc = (0, core_1.readTaskFile)(located.taskPath);
566
+ if (!doc)
567
+ return { success: false, error: `Failed to read task ${taskId}` };
568
+ if (!doc.task.subtasks || doc.task.subtasks.length === 0) {
569
+ return { success: false, error: `Task ${taskId} has no subtasks` };
570
+ }
571
+ const updatedSubtasks = doc.task.subtasks.map((subtask) => {
572
+ if (subtask.id !== subtaskId)
573
+ return subtask;
574
+ return { ...subtask, completed: !subtask.completed };
575
+ });
576
+ const found = updatedSubtasks.some((subtask) => subtask.id === subtaskId);
577
+ if (!found)
578
+ return { success: false, error: `Subtask ${subtaskId} not found` };
579
+ const updatedTask = {
580
+ ...doc.task,
581
+ subtasks: updatedSubtasks,
582
+ updatedAt: new Date().toISOString(),
583
+ };
584
+ try {
585
+ (0, core_1.writeTaskFile)(located.taskPath, updatedTask, doc.body);
586
+ return { success: true, message: `Toggled ${subtaskId}` };
587
+ }
588
+ catch (err) {
589
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
590
+ }
591
+ }
381
592
  const { board, error } = readBrainfile(filePath);
382
593
  if (!board)
383
594
  return { success: false, error };
@@ -393,12 +604,28 @@ function toggleSubtaskAction(filePath, taskId, subtaskId) {
393
604
  * Add a new task to a column (quick add - title only)
394
605
  */
395
606
  function addTaskAction(filePath, columnId, taskInput) {
396
- const { board, error } = readBrainfile(filePath);
607
+ const { board, error } = readBoardState(filePath);
397
608
  if (!board)
398
609
  return { success: false, error };
399
610
  const column = (0, core_1.findColumnById)(board, columnId) || (0, core_1.findColumnByName)(board, columnId);
400
611
  if (!column)
401
612
  return { success: false, error: `Column ${columnId} not found` };
613
+ if ((0, v2_detect_1.isV2)(filePath)) {
614
+ const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
615
+ const result = (0, core_1.addTaskFile)(dirs.boardDir, {
616
+ title: taskInput.title,
617
+ column: column.id,
618
+ description: taskInput.description,
619
+ priority: taskInput.priority,
620
+ tags: taskInput.tags,
621
+ assignee: taskInput.assignee,
622
+ dueDate: taskInput.dueDate,
623
+ relatedFiles: taskInput.relatedFiles,
624
+ }, taskInput.description ? `## Description\n${taskInput.description.trim()}\n` : '', dirs.logsDir);
625
+ if (!result.success)
626
+ return { success: false, error: result.error };
627
+ return { success: true, message: `Added ${result.task?.id || 'task'}` };
628
+ }
402
629
  const result = (0, core_1.addTask)(board, column.id, taskInput);
403
630
  if (!result.success)
404
631
  return { success: false, error: result.error };
@@ -414,7 +641,7 @@ function addTaskAction(filePath, columnId, taskInput) {
414
641
  * Create a new task using $EDITOR with full template
415
642
  */
416
643
  function newTaskInEditor(filePath, columnId) {
417
- const { board, error } = readBrainfile(filePath);
644
+ const { board, error } = readBoardState(filePath);
418
645
  if (!board)
419
646
  return { success: false, error };
420
647
  const column = (0, core_1.findColumnById)(board, columnId) || (0, core_1.findColumnByName)(board, columnId);
@@ -547,7 +774,7 @@ function parseNewTaskYaml(yaml) {
547
774
  * Uses spawnSync to fully block and give terminal control to the editor
548
775
  */
549
776
  function editTaskInEditor(filePath, taskId) {
550
- const { board, error } = readBrainfile(filePath);
777
+ const { board, error } = readBoardState(filePath);
551
778
  if (!board) {
552
779
  return { success: false, error };
553
780
  }
@@ -771,7 +998,7 @@ function copyToClipboard(text) {
771
998
  * Add a new rule to a rule category
772
999
  */
773
1000
  function addRuleAction(filePath, ruleType, ruleText) {
774
- const { board, error } = readBrainfile(filePath);
1001
+ const { board, error } = readBoardConfigOnly(filePath);
775
1002
  if (!board)
776
1003
  return { success: false, error };
777
1004
  // Initialize rules if not present
@@ -805,7 +1032,7 @@ function addRuleAction(filePath, ruleType, ruleText) {
805
1032
  * Update an existing rule
806
1033
  */
807
1034
  function updateRuleAction(filePath, ruleType, ruleId, ruleText) {
808
- const { board, error } = readBrainfile(filePath);
1035
+ const { board, error } = readBoardConfigOnly(filePath);
809
1036
  if (!board)
810
1037
  return { success: false, error };
811
1038
  if (!board.rules || !board.rules[ruleType]) {
@@ -825,7 +1052,7 @@ function updateRuleAction(filePath, ruleType, ruleId, ruleText) {
825
1052
  * Delete a rule
826
1053
  */
827
1054
  function deleteRuleAction(filePath, ruleType, ruleId) {
828
- const { board, error } = readBrainfile(filePath);
1055
+ const { board, error } = readBoardConfigOnly(filePath);
829
1056
  if (!board)
830
1057
  return { success: false, error };
831
1058
  if (!board.rules || !board.rules[ruleType]) {
@@ -845,61 +1072,110 @@ function deleteRuleAction(filePath, ruleType, ruleId) {
845
1072
  // ARCHIVE OPERATIONS
846
1073
  // =============================================================================
847
1074
  /**
848
- * Load archive from the archive file
1075
+ * Load logs from the logs file
849
1076
  */
850
- function loadArchive(filePath) {
1077
+ function loadLogs(filePath) {
1078
+ if ((0, v2_detect_1.isV2)(filePath)) {
1079
+ try {
1080
+ const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
1081
+ const docs = (0, core_1.readTasksDir)(dirs.logsDir);
1082
+ const logs = docs
1083
+ .map((doc) => ({ ...doc.task }))
1084
+ .sort((a, b) => {
1085
+ const aTime = a.completedAt ? Date.parse(a.completedAt) : 0;
1086
+ const bTime = b.completedAt ? Date.parse(b.completedAt) : 0;
1087
+ return bTime - aTime;
1088
+ });
1089
+ return { logs };
1090
+ }
1091
+ catch (err) {
1092
+ return { logs: [], error: err instanceof Error ? err.message : String(err) };
1093
+ }
1094
+ }
851
1095
  const archivePath = getArchivePath(filePath);
852
1096
  if (!fs.existsSync(archivePath)) {
853
- return { archive: [] };
1097
+ return { logs: [] };
854
1098
  }
855
1099
  try {
856
1100
  const archiveContent = fs.readFileSync(archivePath, 'utf-8');
857
1101
  const parseResult = core_1.Brainfile.parseWithErrors(archiveContent);
858
1102
  if (parseResult.board && parseResult.board.archive) {
859
- return { archive: parseResult.board.archive };
1103
+ return { logs: parseResult.board.archive };
860
1104
  }
861
- return { archive: [] };
1105
+ return { logs: [] };
862
1106
  }
863
1107
  catch (err) {
864
- return { archive: [], error: err instanceof Error ? err.message : String(err) };
1108
+ return { logs: [], error: err instanceof Error ? err.message : String(err) };
865
1109
  }
866
1110
  }
867
1111
  /**
868
- * Restore a task from the archive to a column
1112
+ * Restore a task from logs to a column
869
1113
  */
870
1114
  function restoreTaskAction(filePath, taskId, toColumnId) {
1115
+ if ((0, v2_detect_1.isV2)(filePath)) {
1116
+ const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
1117
+ const { board, error } = readBoardState(filePath);
1118
+ if (!board)
1119
+ return { success: false, error };
1120
+ const column = (0, core_1.findColumnById)(board, toColumnId) || (0, core_1.findColumnByName)(board, toColumnId);
1121
+ if (!column) {
1122
+ return { success: false, error: `Column ${toColumnId} not found` };
1123
+ }
1124
+ if ((0, core_1.findTask)(dirs.boardDir, taskId)) {
1125
+ return { success: false, error: `Task ${taskId} already exists in board` };
1126
+ }
1127
+ const logDoc = (0, core_1.findTask)(dirs.logsDir, taskId);
1128
+ if (!logDoc || !logDoc.filePath) {
1129
+ return { success: false, error: `Task ${taskId} not found in logs` };
1130
+ }
1131
+ const restoredTask = {
1132
+ ...logDoc.task,
1133
+ column: column.id,
1134
+ updatedAt: new Date().toISOString(),
1135
+ };
1136
+ delete restoredTask.completedAt;
1137
+ const targetPath = path.join(dirs.boardDir, `${taskId}.md`);
1138
+ try {
1139
+ (0, core_1.writeTaskFile)(targetPath, restoredTask, logDoc.body);
1140
+ fs.unlinkSync(logDoc.filePath);
1141
+ return { success: true, message: `Restored ${taskId} to ${column.title}` };
1142
+ }
1143
+ catch (err) {
1144
+ return { success: false, error: `Failed to restore task: ${err instanceof Error ? err.message : String(err)}` };
1145
+ }
1146
+ }
871
1147
  const archivePath = getArchivePath(filePath);
872
1148
  // Read archive file
873
1149
  if (!fs.existsSync(archivePath)) {
874
- return { success: false, error: 'Archive file not found' };
1150
+ return { success: false, error: 'Logs file not found' };
875
1151
  }
876
1152
  let archiveBoard;
877
1153
  try {
878
1154
  const archiveContent = fs.readFileSync(archivePath, 'utf-8');
879
1155
  const parseResult = core_1.Brainfile.parseWithErrors(archiveContent);
880
1156
  if (!parseResult.board) {
881
- return { success: false, error: 'Failed to parse archive file' };
1157
+ return { success: false, error: 'Failed to parse logs file' };
882
1158
  }
883
1159
  archiveBoard = parseResult.board;
884
1160
  }
885
1161
  catch (err) {
886
- return { success: false, error: `Failed to read archive: ${err instanceof Error ? err.message : String(err)}` };
1162
+ return { success: false, error: `Failed to read logs: ${err instanceof Error ? err.message : String(err)}` };
887
1163
  }
888
1164
  // Find task in archive
889
1165
  if (!archiveBoard.archive || archiveBoard.archive.length === 0) {
890
- return { success: false, error: 'Archive is empty' };
1166
+ return { success: false, error: 'Logs are empty' };
891
1167
  }
892
1168
  const taskIndex = archiveBoard.archive.findIndex(t => t.id === taskId);
893
1169
  if (taskIndex === -1) {
894
- return { success: false, error: `Task ${taskId} not found in archive` };
1170
+ return { success: false, error: `Task ${taskId} not found in logs` };
895
1171
  }
896
1172
  const task = archiveBoard.archive[taskIndex];
897
1173
  // Read main board
898
- const { board, error } = readBrainfile(filePath);
899
- if (!board)
900
- return { success: false, error };
1174
+ const { board: mainBoard, error: mainBoardError } = readBrainfile(filePath);
1175
+ if (!mainBoard)
1176
+ return { success: false, error: mainBoardError };
901
1177
  // Find target column
902
- const column = (0, core_1.findColumnById)(board, toColumnId) || (0, core_1.findColumnByName)(board, toColumnId);
1178
+ const column = (0, core_1.findColumnById)(mainBoard, toColumnId) || (0, core_1.findColumnByName)(mainBoard, toColumnId);
903
1179
  if (!column) {
904
1180
  return { success: false, error: `Column ${toColumnId} not found` };
905
1181
  }
@@ -908,7 +1184,7 @@ function restoreTaskAction(filePath, taskId, toColumnId) {
908
1184
  // Remove from archive
909
1185
  archiveBoard.archive.splice(taskIndex, 1);
910
1186
  // Save both files
911
- const writeResult = writeBrainfile(filePath, board);
1187
+ const writeResult = writeBrainfile(filePath, mainBoard);
912
1188
  if (!writeResult.success)
913
1189
  return writeResult;
914
1190
  try {
@@ -916,38 +1192,50 @@ function restoreTaskAction(filePath, taskId, toColumnId) {
916
1192
  fs.writeFileSync(archivePath, archiveContent, 'utf-8');
917
1193
  }
918
1194
  catch (err) {
919
- return { success: false, error: `Task restored but failed to update archive: ${err}` };
1195
+ return { success: false, error: `Task restored but failed to update logs: ${err}` };
920
1196
  }
921
1197
  return { success: true, message: `Restored ${taskId} to ${column.title}` };
922
1198
  }
923
1199
  /**
924
- * Permanently delete a task from the archive
1200
+ * Permanently delete a task from logs
925
1201
  */
926
1202
  function deleteArchivedTaskAction(filePath, taskId) {
1203
+ if ((0, v2_detect_1.isV2)(filePath)) {
1204
+ const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
1205
+ const logDoc = (0, core_1.findTask)(dirs.logsDir, taskId);
1206
+ if (!logDoc || !logDoc.filePath) {
1207
+ return { success: false, error: `Task ${taskId} not found in logs` };
1208
+ }
1209
+ const result = (0, core_1.deleteTaskFile)(logDoc.filePath);
1210
+ if (!result.success) {
1211
+ return { success: false, error: result.error || `Failed to delete ${taskId}` };
1212
+ }
1213
+ return { success: true, message: `Permanently deleted ${taskId}` };
1214
+ }
927
1215
  const archivePath = getArchivePath(filePath);
928
1216
  // Read archive file
929
1217
  if (!fs.existsSync(archivePath)) {
930
- return { success: false, error: 'Archive file not found' };
1218
+ return { success: false, error: 'Logs file not found' };
931
1219
  }
932
1220
  let archiveBoard;
933
1221
  try {
934
1222
  const archiveContent = fs.readFileSync(archivePath, 'utf-8');
935
1223
  const parseResult = core_1.Brainfile.parseWithErrors(archiveContent);
936
1224
  if (!parseResult.board) {
937
- return { success: false, error: 'Failed to parse archive file' };
1225
+ return { success: false, error: 'Failed to parse logs file' };
938
1226
  }
939
1227
  archiveBoard = parseResult.board;
940
1228
  }
941
1229
  catch (err) {
942
- return { success: false, error: `Failed to read archive: ${err instanceof Error ? err.message : String(err)}` };
1230
+ return { success: false, error: `Failed to read logs: ${err instanceof Error ? err.message : String(err)}` };
943
1231
  }
944
1232
  // Find and remove task from archive
945
1233
  if (!archiveBoard.archive || archiveBoard.archive.length === 0) {
946
- return { success: false, error: 'Archive is empty' };
1234
+ return { success: false, error: 'Logs are empty' };
947
1235
  }
948
1236
  const taskIndex = archiveBoard.archive.findIndex(t => t.id === taskId);
949
1237
  if (taskIndex === -1) {
950
- return { success: false, error: `Task ${taskId} not found in archive` };
1238
+ return { success: false, error: `Task ${taskId} not found in logs` };
951
1239
  }
952
1240
  archiveBoard.archive.splice(taskIndex, 1);
953
1241
  // Save archive file
@@ -956,7 +1244,7 @@ function deleteArchivedTaskAction(filePath, taskId) {
956
1244
  fs.writeFileSync(archivePath, archiveContent, 'utf-8');
957
1245
  }
958
1246
  catch (err) {
959
- return { success: false, error: `Failed to update archive: ${err}` };
1247
+ return { success: false, error: `Failed to update logs: ${err}` };
960
1248
  }
961
1249
  return { success: true, message: `Permanently deleted ${taskId}` };
962
1250
  }