@brainfile/cli 0.13.3 → 0.15.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.
- package/CHANGELOG.md +26 -0
- package/README.md +123 -358
- package/dist/cli.js +88 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts +3 -0
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +162 -3
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/adr.d.ts +22 -0
- package/dist/commands/adr.d.ts.map +1 -0
- package/dist/commands/adr.js +182 -0
- package/dist/commands/adr.js.map +1 -0
- package/dist/commands/archive.d.ts.map +1 -1
- package/dist/commands/archive.js +147 -0
- package/dist/commands/archive.js.map +1 -1
- package/dist/commands/complete.d.ts +30 -0
- package/dist/commands/complete.d.ts.map +1 -0
- package/dist/commands/complete.js +254 -0
- package/dist/commands/complete.js.map +1 -0
- package/dist/commands/contract.d.ts.map +1 -1
- package/dist/commands/contract.js +2 -0
- package/dist/commands/contract.js.map +1 -1
- package/dist/commands/delete.d.ts.map +1 -1
- package/dist/commands/delete.js +29 -1
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +41 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +34 -12
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/log.d.ts +52 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +246 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +864 -44
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/migrate.d.ts +4 -3
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +193 -38
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/move.d.ts.map +1 -1
- package/dist/commands/move.js +90 -0
- package/dist/commands/move.js.map +1 -1
- package/dist/commands/patch.d.ts.map +1 -1
- package/dist/commands/patch.js +85 -13
- package/dist/commands/patch.js.map +1 -1
- package/dist/commands/search.d.ts +33 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +209 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +74 -1
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/subtask.d.ts.map +1 -1
- package/dist/commands/subtask.js +72 -5
- package/dist/commands/subtask.js.map +1 -1
- package/dist/commands/types.d.ts +40 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +242 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/contractRunner.d.ts.map +1 -1
- package/dist/lib/contractRunner.js +177 -12
- package/dist/lib/contractRunner.js.map +1 -1
- package/dist/tui/BrainfileTUI.d.ts.map +1 -1
- package/dist/tui/BrainfileTUI.js +23 -20
- package/dist/tui/BrainfileTUI.js.map +1 -1
- package/dist/tui/actions.d.ts +5 -5
- package/dist/tui/actions.d.ts.map +1 -1
- package/dist/tui/actions.js +335 -47
- package/dist/tui/actions.js.map +1 -1
- package/dist/tui/components/ArchivePanel.js +1 -1
- package/dist/tui/components/ArchivePanel.js.map +1 -1
- package/dist/tui/components/ColumnTabs.d.ts.map +1 -1
- package/dist/tui/components/ColumnTabs.js +6 -2
- package/dist/tui/components/ColumnTabs.js.map +1 -1
- package/dist/tui/components/HelpOverlay.js +3 -3
- package/dist/tui/components/HelpOverlay.js.map +1 -1
- package/dist/tui/components/LogsPanel.d.ts +16 -0
- package/dist/tui/components/LogsPanel.d.ts.map +1 -0
- package/dist/tui/components/LogsPanel.js +115 -0
- package/dist/tui/components/LogsPanel.js.map +1 -0
- package/dist/tui/components/MainPanelTabs.d.ts +2 -2
- package/dist/tui/components/MainPanelTabs.d.ts.map +1 -1
- package/dist/tui/components/MainPanelTabs.js +3 -3
- package/dist/tui/components/MainPanelTabs.js.map +1 -1
- package/dist/tui/components/StackedTaskList.d.ts +2 -1
- package/dist/tui/components/StackedTaskList.d.ts.map +1 -1
- package/dist/tui/components/StackedTaskList.js +9 -9
- package/dist/tui/components/StackedTaskList.js.map +1 -1
- package/dist/tui/components/TaskCard.d.ts +2 -1
- package/dist/tui/components/TaskCard.d.ts.map +1 -1
- package/dist/tui/components/TaskCard.js +41 -5
- package/dist/tui/components/TaskCard.js.map +1 -1
- package/dist/tui/components/TaskCardMeasure.d.ts +2 -16
- package/dist/tui/components/TaskCardMeasure.d.ts.map +1 -1
- package/dist/tui/components/TaskCardMeasure.js +30 -25
- package/dist/tui/components/TaskCardMeasure.js.map +1 -1
- package/dist/tui/components/TaskDetail.d.ts +2 -3
- package/dist/tui/components/TaskDetail.d.ts.map +1 -1
- package/dist/tui/components/TaskDetail.js +35 -12
- package/dist/tui/components/TaskDetail.js.map +1 -1
- package/dist/tui/components/TaskList.d.ts +2 -1
- package/dist/tui/components/TaskList.d.ts.map +1 -1
- package/dist/tui/components/TaskList.js +5 -5
- package/dist/tui/components/TaskList.js.map +1 -1
- package/dist/tui/components/index.d.ts +2 -2
- package/dist/tui/components/index.d.ts.map +1 -1
- package/dist/tui/components/index.js +3 -3
- package/dist/tui/components/index.js.map +1 -1
- package/dist/tui/hooks/useBrainfileLoader.d.ts.map +1 -1
- package/dist/tui/hooks/useBrainfileLoader.js +97 -31
- package/dist/tui/hooks/useBrainfileLoader.js.map +1 -1
- package/dist/tui/hooks/useKeyboardNavigation.d.ts.map +1 -1
- package/dist/tui/hooks/useKeyboardNavigation.js +47 -47
- package/dist/tui/hooks/useKeyboardNavigation.js.map +1 -1
- package/dist/tui/types.d.ts +7 -7
- package/dist/tui/types.d.ts.map +1 -1
- package/dist/utils/board-types.d.ts +13 -0
- package/dist/utils/board-types.d.ts.map +1 -0
- package/dist/utils/board-types.js +7 -0
- package/dist/utils/board-types.js.map +1 -0
- package/dist/utils/dot-brainfile.d.ts +9 -0
- package/dist/utils/dot-brainfile.d.ts.map +1 -0
- package/dist/utils/dot-brainfile.js +74 -0
- package/dist/utils/dot-brainfile.js.map +1 -0
- package/dist/utils/strict-validation.d.ts +8 -0
- package/dist/utils/strict-validation.d.ts.map +1 -0
- package/dist/utils/strict-validation.js +41 -0
- package/dist/utils/strict-validation.js.map +1 -0
- package/dist/utils/v2-detect.d.ts +28 -0
- package/dist/utils/v2-detect.d.ts.map +1 -0
- package/dist/utils/v2-detect.js +109 -0
- package/dist/utils/v2-detect.js.map +1 -0
- package/dist/utils/v2-tasks.d.ts +121 -0
- package/dist/utils/v2-tasks.d.ts.map +1 -0
- package/dist/utils/v2-tasks.js +384 -0
- package/dist/utils/v2-tasks.js.map +1 -0
- package/package.json +3 -3
- package/state.json +3 -0
package/dist/tui/actions.js
CHANGED
|
@@ -47,7 +47,7 @@ exports.copyToClipboard = copyToClipboard;
|
|
|
47
47
|
exports.addRuleAction = addRuleAction;
|
|
48
48
|
exports.updateRuleAction = updateRuleAction;
|
|
49
49
|
exports.deleteRuleAction = deleteRuleAction;
|
|
50
|
-
exports.
|
|
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 } =
|
|
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 } =
|
|
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: '
|
|
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
|
|
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: `
|
|
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 } =
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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
|
|
1075
|
+
* Load logs from the logs file
|
|
849
1076
|
*/
|
|
850
|
-
function
|
|
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 {
|
|
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 {
|
|
1103
|
+
return { logs: parseResult.board.archive };
|
|
860
1104
|
}
|
|
861
|
-
return {
|
|
1105
|
+
return { logs: [] };
|
|
862
1106
|
}
|
|
863
1107
|
catch (err) {
|
|
864
|
-
return {
|
|
1108
|
+
return { logs: [], error: err instanceof Error ? err.message : String(err) };
|
|
865
1109
|
}
|
|
866
1110
|
}
|
|
867
1111
|
/**
|
|
868
|
-
* Restore a task from
|
|
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: '
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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 (!
|
|
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)(
|
|
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,
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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
|
|
1247
|
+
return { success: false, error: `Failed to update logs: ${err}` };
|
|
960
1248
|
}
|
|
961
1249
|
return { success: true, message: `Permanently deleted ${taskId}` };
|
|
962
1250
|
}
|