@wrongstack/tools 0.302.0 → 0.303.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/dist/builtin.js +2799 -609
- package/dist/codebase-index/binary-frame.d.ts +43 -0
- package/dist/codebase-index/codebase-incoming-calls-tool.d.ts +1 -0
- package/dist/codebase-index/codebase-outgoing-calls-tool.d.ts +1 -0
- package/dist/codebase-index/content-hash.d.ts +66 -0
- package/dist/codebase-index/index.js +1569 -148
- package/dist/codebase-index/parser-worker-pool.d.ts +63 -0
- package/dist/codebase-index/parser-worker-script.d.ts +42 -0
- package/dist/codebase-index/project-server-protocol.d.ts +2 -0
- package/dist/codebase-index/project-server.js +1455 -99
- package/dist/codebase-index/schema.d.ts +7 -0
- package/dist/codebase-index/tree-sitter/queries.d.ts +48 -0
- package/dist/codebase-index/tree-sitter/util.d.ts +31 -0
- package/dist/codebase-index/tree-sitter/visitor.d.ts +47 -0
- package/dist/codebase-index/tree-sitter-parser.d.ts +58 -0
- package/dist/codebase-index/vector-search.d.ts +62 -0
- package/dist/codebase-index/worker-protocol.d.ts +2 -0
- package/dist/codebase-index/worker.js +1424 -68
- package/dist/codebase-index/writer-bulk-insert.d.ts +5 -0
- package/dist/codebase-index/writer-graph-reader.d.ts +39 -0
- package/dist/codebase-index/writer-schema.d.ts +9 -2
- package/dist/codebase-index/writer.d.ts +36 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2785 -634
- package/dist/kanban-task-inputs.d.ts +1 -0
- package/dist/kanban-tool-schema.d.ts +2 -2
- package/dist/kanban-tool-types.d.ts +18 -2
- package/dist/kanban.js +392 -126
- package/dist/pack.js +2799 -609
- package/dist/plan.d.ts +4 -1
- package/dist/plan.js +2380 -9
- package/dist/read.js +1531 -98
- package/dist/session-kanban.d.ts +8 -0
- package/dist/session-kanban.js +111 -17
- package/dist/task.d.ts +5 -4
- package/dist/task.js +2418 -43
- package/dist/todo.d.ts +10 -1
- package/dist/todo.js +2152 -20
- package/dist/tool-tier.js +2799 -609
- package/package.json +8 -4
package/dist/session-kanban.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ import { type KanbanBoard, type KanbanColumn, type KanbanTask } from '@wrongstac
|
|
|
7
7
|
export declare const SESSION_KANBAN_COLUMNS: KanbanColumn[];
|
|
8
8
|
/** Create (or migrate) the single Kanban board owned by a session. */
|
|
9
9
|
export declare function ensureSessionKanbanBoard(projectRoot: string | undefined, sessionId: string): Promise<KanbanBoard | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Remove an inactive session's observational mirror, including its cards.
|
|
12
|
+
* The durable source remains the session todo/task/plan files; retaining the
|
|
13
|
+
* mirror after detach only creates historical duplicate work in project views.
|
|
14
|
+
*/
|
|
15
|
+
export declare function cleanupSessionKanbanBoard(projectRoot: string | undefined, sessionId: string): Promise<string[]>;
|
|
10
16
|
/** Remove a particular inactive session's system-owned board when it has no cards. */
|
|
11
17
|
export declare function cleanupSessionKanbanBoardIfEmpty(projectRoot: string | undefined, sessionId: string): Promise<string[]>;
|
|
12
18
|
/** Prune stale empty session boards while preserving manual and live boards. */
|
|
@@ -43,6 +49,8 @@ export interface SessionKanbanSourceUpdate {
|
|
|
43
49
|
* todo list that happened to exist when the run started.
|
|
44
50
|
*/
|
|
45
51
|
export declare function applySessionKanbanBoardToTodos(context: Context, board: KanbanBoard): TodoItem[];
|
|
52
|
+
/** Project a durable managed board into the session's compact todo surface. */
|
|
53
|
+
export declare function applyManagedKanbanBoardToTodos(context: Context, board: KanbanBoard): TodoItem[];
|
|
46
54
|
/** Reflect a TUI/WebUI Kanban card edit back to its originating work list. */
|
|
47
55
|
export declare function applySessionKanbanTaskToSource(context: Context, task: KanbanTask, options?: {
|
|
48
56
|
remove?: boolean | undefined;
|
package/dist/session-kanban.js
CHANGED
|
@@ -135,6 +135,26 @@ async function removeEmptySessionBoard(projectRoot, boardId, sessionId) {
|
|
|
135
135
|
return await removeBoard(projectRoot, board.id) ? board.id : null;
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
|
+
async function removeOwnedSessionBoard(projectRoot, boardId, sessionId) {
|
|
139
|
+
return enqueueBoardWork(projectRoot, sessionId, async () => {
|
|
140
|
+
if (isSessionBoardActive(projectRoot, sessionId)) return null;
|
|
141
|
+
const board = await getBoard(projectRoot, boardId);
|
|
142
|
+
if (!board || !isOwnedSessionBoard(board.tags)) return null;
|
|
143
|
+
if (sessionIdFromTags(board.tags) !== sessionId) return null;
|
|
144
|
+
return await removeBoard(projectRoot, board.id) ? board.id : null;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async function cleanupSessionKanbanBoard(projectRoot, sessionId) {
|
|
148
|
+
if (!projectRoot || !sessionId || process.env[MIRROR_DISABLED_ENV] === "0") return [];
|
|
149
|
+
if (isSessionBoardActive(projectRoot, sessionId)) return [];
|
|
150
|
+
const candidates = (await listBoards(projectRoot)).filter(
|
|
151
|
+
(board) => isOwnedSessionBoard(board.tags) && sessionIdFromTags(board.tags) === sessionId
|
|
152
|
+
);
|
|
153
|
+
const removed = await Promise.all(
|
|
154
|
+
candidates.map((board) => removeOwnedSessionBoard(projectRoot, board.id, sessionId))
|
|
155
|
+
);
|
|
156
|
+
return removed.filter((boardId) => Boolean(boardId));
|
|
157
|
+
}
|
|
138
158
|
async function cleanupSessionKanbanBoardIfEmpty(projectRoot, sessionId) {
|
|
139
159
|
if (!projectRoot || !sessionId || process.env[MIRROR_DISABLED_ENV] === "0") return [];
|
|
140
160
|
if (isSessionBoardActive(projectRoot, sessionId)) return [];
|
|
@@ -197,7 +217,17 @@ function queueLatestMirror(projectRoot, sessionId, graph, sourceSystem) {
|
|
|
197
217
|
pending.graph,
|
|
198
218
|
pending.sourceSystem
|
|
199
219
|
);
|
|
200
|
-
} catch {
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.warn(
|
|
222
|
+
JSON.stringify({
|
|
223
|
+
level: "warn",
|
|
224
|
+
event: "session-kanban.mirror-failed",
|
|
225
|
+
sessionId: pending.sessionId,
|
|
226
|
+
sourceSystem: pending.sourceSystem,
|
|
227
|
+
message: error instanceof Error ? error.message : String(error),
|
|
228
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
229
|
+
})
|
|
230
|
+
);
|
|
201
231
|
}
|
|
202
232
|
}
|
|
203
233
|
} finally {
|
|
@@ -216,6 +246,7 @@ function queueLatestMirror(projectRoot, sessionId, graph, sourceSystem) {
|
|
|
216
246
|
})();
|
|
217
247
|
}
|
|
218
248
|
function todoListToSerializedGraph(todos, sessionId) {
|
|
249
|
+
const graphId = `todo:${sessionId}`;
|
|
219
250
|
const nodes = todos.map((todo, index) => ({
|
|
220
251
|
id: todo.id,
|
|
221
252
|
title: todo.content,
|
|
@@ -223,12 +254,14 @@ function todoListToSerializedGraph(todos, sessionId) {
|
|
|
223
254
|
type: "chore",
|
|
224
255
|
priority: "medium",
|
|
225
256
|
status: todo.status,
|
|
257
|
+
specRequirementId: `${graphId}:${todo.id}`,
|
|
226
258
|
createdAt: index,
|
|
227
259
|
updatedAt: index
|
|
228
260
|
}));
|
|
229
261
|
return {
|
|
230
|
-
id:
|
|
231
|
-
specId:
|
|
262
|
+
id: graphId,
|
|
263
|
+
specId: graphId,
|
|
264
|
+
requiredRequirementIds: nodes.map((node) => node.specRequirementId),
|
|
232
265
|
title: "Session todos",
|
|
233
266
|
nodes,
|
|
234
267
|
edges: [],
|
|
@@ -238,6 +271,7 @@ function todoListToSerializedGraph(todos, sessionId) {
|
|
|
238
271
|
};
|
|
239
272
|
}
|
|
240
273
|
function taskFileToSerializedGraph(tasks, sessionId) {
|
|
274
|
+
const graphId = `session:${sessionId}`;
|
|
241
275
|
const ids = new Set(tasks.map((task) => task.id));
|
|
242
276
|
const nodes = tasks.map((task, index) => ({
|
|
243
277
|
id: task.id,
|
|
@@ -246,6 +280,7 @@ function taskFileToSerializedGraph(tasks, sessionId) {
|
|
|
246
280
|
type: task.type,
|
|
247
281
|
priority: task.priority,
|
|
248
282
|
status: task.status,
|
|
283
|
+
specRequirementId: `${graphId}:${task.id}`,
|
|
249
284
|
...task.assignee ? { assignee: task.assignee } : {},
|
|
250
285
|
...task.estimateHours !== void 0 ? { estimateHours: task.estimateHours } : {},
|
|
251
286
|
createdAt: index,
|
|
@@ -263,8 +298,9 @@ function taskFileToSerializedGraph(tasks, sessionId) {
|
|
|
263
298
|
const rootNodes = nodes.filter((node) => !hasIncoming.has(node.id)).map((node) => node.id);
|
|
264
299
|
return {
|
|
265
300
|
// Keep the historical graph id so existing mirrored task cards are reused.
|
|
266
|
-
id:
|
|
267
|
-
specId:
|
|
301
|
+
id: graphId,
|
|
302
|
+
specId: graphId,
|
|
303
|
+
requiredRequirementIds: nodes.map((node) => node.specRequirementId),
|
|
268
304
|
title: "Session tasks",
|
|
269
305
|
nodes,
|
|
270
306
|
edges,
|
|
@@ -279,6 +315,7 @@ var PLAN_STATUS_TO_TASK = {
|
|
|
279
315
|
done: "completed"
|
|
280
316
|
};
|
|
281
317
|
function planFileToSerializedGraph(items, sessionId) {
|
|
318
|
+
const graphId = `plan:${sessionId}`;
|
|
282
319
|
const nodes = items.map((item, index) => ({
|
|
283
320
|
id: item.id,
|
|
284
321
|
title: item.title,
|
|
@@ -286,12 +323,14 @@ function planFileToSerializedGraph(items, sessionId) {
|
|
|
286
323
|
type: "chore",
|
|
287
324
|
priority: "medium",
|
|
288
325
|
status: PLAN_STATUS_TO_TASK[item.status],
|
|
326
|
+
specRequirementId: `${graphId}:${item.id}`,
|
|
289
327
|
createdAt: index,
|
|
290
328
|
updatedAt: index
|
|
291
329
|
}));
|
|
292
330
|
return {
|
|
293
|
-
id:
|
|
294
|
-
specId:
|
|
331
|
+
id: graphId,
|
|
332
|
+
specId: graphId,
|
|
333
|
+
requiredRequirementIds: nodes.map((node) => node.specRequirementId),
|
|
295
334
|
title: "Session plan",
|
|
296
335
|
nodes,
|
|
297
336
|
edges: [],
|
|
@@ -417,7 +456,7 @@ function attachSessionKanbanMirror(context) {
|
|
|
417
456
|
releaseActiveSessionBoard(attachedProjectRoot, registeredSessionId);
|
|
418
457
|
fireAndForget(
|
|
419
458
|
"cleanup-board",
|
|
420
|
-
|
|
459
|
+
cleanupSessionKanbanBoard(attachedProjectRoot, registeredSessionId)
|
|
421
460
|
);
|
|
422
461
|
}
|
|
423
462
|
registeredSessionId = currentSessionId;
|
|
@@ -434,6 +473,11 @@ function attachSessionKanbanMirror(context) {
|
|
|
434
473
|
let boardTimer = null;
|
|
435
474
|
let presenceTimer = null;
|
|
436
475
|
const sessionId = () => context.session?.id ?? "";
|
|
476
|
+
const activeManagedBoardId = () => {
|
|
477
|
+
const metaKanban = context.meta["kanban"];
|
|
478
|
+
const metaBoardId = metaKanban && typeof metaKanban === "object" ? metaKanban["boardId"] : void 0;
|
|
479
|
+
return context.currentKanbanBoardId ?? (typeof metaBoardId === "string" && metaBoardId ? metaBoardId : "");
|
|
480
|
+
};
|
|
437
481
|
const refreshFiles = async () => {
|
|
438
482
|
const id = sessionId();
|
|
439
483
|
if (!id) return;
|
|
@@ -451,12 +495,19 @@ function attachSessionKanbanMirror(context) {
|
|
|
451
495
|
const refreshBoard = async () => {
|
|
452
496
|
if (!watchedBoardId) return;
|
|
453
497
|
const board = await getBoard(context.projectRoot, watchedBoardId);
|
|
454
|
-
if (board)
|
|
498
|
+
if (!board) return;
|
|
499
|
+
if (board.lifecycle?.mode === "managed") applyManagedKanbanBoardToTodos(context, board);
|
|
500
|
+
else applySessionKanbanBoardToTodos(context, board);
|
|
455
501
|
};
|
|
456
502
|
const configureBoardWatcher = async () => {
|
|
457
503
|
const id = sessionId();
|
|
458
|
-
const
|
|
459
|
-
|
|
504
|
+
const managedBoardId = activeManagedBoardId();
|
|
505
|
+
const board = managedBoardId ? await getBoard(context.projectRoot, managedBoardId) : id ? await ensureSessionKanbanBoard(context.projectRoot, id) : null;
|
|
506
|
+
if (!board) return;
|
|
507
|
+
if (board.id === watchedBoardId) {
|
|
508
|
+
await refreshBoard();
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
460
511
|
unsubscribeBoardEvents?.();
|
|
461
512
|
unsubscribeBoardEvents = null;
|
|
462
513
|
watchedBoardId = board.id;
|
|
@@ -480,6 +531,7 @@ function attachSessionKanbanMirror(context) {
|
|
|
480
531
|
if (presenceTimer) clearInterval(presenceTimer);
|
|
481
532
|
presenceTimer = setInterval(() => fireAndForget("touch-presence", touchPresence()), 6e4);
|
|
482
533
|
presenceTimer.unref?.();
|
|
534
|
+
if (board.lifecycle?.mode === "managed") applyManagedKanbanBoardToTodos(context, board);
|
|
483
535
|
} catch {
|
|
484
536
|
unsubscribeBoardEvents = null;
|
|
485
537
|
watchedBoardId = "";
|
|
@@ -512,6 +564,12 @@ function attachSessionKanbanMirror(context) {
|
|
|
512
564
|
};
|
|
513
565
|
const unsubscribe = context.state.onChange((change) => {
|
|
514
566
|
if (change.kind === "todos_replaced" && !suppressedTodoMirrors.has(context)) {
|
|
567
|
+
const snapshot = change.completedSnapshot ?? change.todos;
|
|
568
|
+
if (snapshot.length > 0 && snapshot.every(
|
|
569
|
+
(todo) => todo.kanbanBoardId === activeManagedBoardId() && Boolean(todo.kanbanTaskId)
|
|
570
|
+
)) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
515
573
|
mirrorSessionTodosToKanban(
|
|
516
574
|
context.projectRoot,
|
|
517
575
|
change.completedSnapshot ?? change.todos,
|
|
@@ -519,11 +577,14 @@ function attachSessionKanbanMirror(context) {
|
|
|
519
577
|
);
|
|
520
578
|
return;
|
|
521
579
|
}
|
|
522
|
-
if (change.kind === "meta_set" && (change.key === "plan.path" || change.key === "task.path")) {
|
|
580
|
+
if (change.kind === "meta_set" && (change.key === "plan.path" || change.key === "task.path" || change.key === "kanban")) {
|
|
523
581
|
syncActiveSessionRegistration();
|
|
524
582
|
configureWatcher();
|
|
525
|
-
|
|
583
|
+
if (!activeManagedBoardId()) {
|
|
584
|
+
fireAndForget("ensure-board", ensureSessionKanbanBoard(context.projectRoot, sessionId()));
|
|
585
|
+
}
|
|
526
586
|
fireAndForget("configure-watcher", configureBoardWatcher());
|
|
587
|
+
fireAndForget("refresh-board", refreshBoard());
|
|
527
588
|
fireAndForget("refresh-files", refreshFiles());
|
|
528
589
|
}
|
|
529
590
|
});
|
|
@@ -541,7 +602,7 @@ function attachSessionKanbanMirror(context) {
|
|
|
541
602
|
releaseActiveSessionBoard(attachedProjectRoot, registeredSessionId);
|
|
542
603
|
fireAndForget(
|
|
543
604
|
"cleanup-board",
|
|
544
|
-
|
|
605
|
+
cleanupSessionKanbanBoard(attachedProjectRoot, registeredSessionId)
|
|
545
606
|
);
|
|
546
607
|
registeredSessionId = "";
|
|
547
608
|
}
|
|
@@ -583,18 +644,26 @@ function todoStatus(task) {
|
|
|
583
644
|
if (status === "in_progress" || status === "review") return "in_progress";
|
|
584
645
|
return "pending";
|
|
585
646
|
}
|
|
586
|
-
function sessionTodoFromTask(task) {
|
|
647
|
+
function sessionTodoFromTask(task, boardId) {
|
|
587
648
|
return {
|
|
588
649
|
id: task.origin?.taskId ?? task.id,
|
|
589
650
|
content: task.title,
|
|
590
651
|
status: todoStatus(task),
|
|
652
|
+
kanbanBoardId: boardId,
|
|
653
|
+
kanbanTaskId: task.id,
|
|
591
654
|
...task.description ? { activeForm: task.description } : {}
|
|
592
655
|
};
|
|
593
656
|
}
|
|
657
|
+
function managedTodoFromTask(task, boardId) {
|
|
658
|
+
return {
|
|
659
|
+
...sessionTodoFromTask(task, boardId),
|
|
660
|
+
status: task.status === "completed" ? "completed" : task.status === "in_progress" ? "in_progress" : "pending"
|
|
661
|
+
};
|
|
662
|
+
}
|
|
594
663
|
function sameTodos(left, right) {
|
|
595
664
|
return left.length === right.length && left.every((todo, index) => {
|
|
596
665
|
const candidate = right[index];
|
|
597
|
-
return candidate?.id === todo.id && candidate.content === todo.content && candidate.status === todo.status && candidate.activeForm === todo.activeForm && candidate.promotedFromPlan === todo.promotedFromPlan && candidate.promotedFromTask === todo.promotedFromTask;
|
|
666
|
+
return candidate?.id === todo.id && candidate.content === todo.content && candidate.status === todo.status && candidate.activeForm === todo.activeForm && candidate.promotedFromPlan === todo.promotedFromPlan && candidate.promotedFromTask === todo.promotedFromTask && candidate.kanbanBoardId === todo.kanbanBoardId && candidate.kanbanTaskId === todo.kanbanTaskId;
|
|
598
667
|
});
|
|
599
668
|
}
|
|
600
669
|
function applySessionKanbanBoardToTodos(context, board) {
|
|
@@ -608,7 +677,7 @@ function applySessionKanbanBoardToTodos(context, board) {
|
|
|
608
677
|
const leftColumn = board.columns.find((column) => column.id === left.columnId)?.order ?? 0;
|
|
609
678
|
const rightColumn = board.columns.find((column) => column.id === right.columnId)?.order ?? 0;
|
|
610
679
|
return leftColumn - rightColumn || left.order - right.order || left.createdAt.localeCompare(right.createdAt);
|
|
611
|
-
}).map(sessionTodoFromTask);
|
|
680
|
+
}).map((task) => sessionTodoFromTask(task, board.id));
|
|
612
681
|
const allCompleted = projectedTodos.length > 0 && projectedTodos.every((todo) => todo.status === "completed");
|
|
613
682
|
const effectiveTodos = allCompleted ? [] : projectedTodos;
|
|
614
683
|
if (sameTodos(context.todos, effectiveTodos)) return [...context.todos];
|
|
@@ -622,6 +691,29 @@ function applySessionKanbanBoardToTodos(context, board) {
|
|
|
622
691
|
broadcastTodoUpdate(context, context.todos);
|
|
623
692
|
return [...context.todos];
|
|
624
693
|
}
|
|
694
|
+
function applyManagedKanbanBoardToTodos(context, board) {
|
|
695
|
+
const metaKanban = context.meta["kanban"];
|
|
696
|
+
const metaBoardId = metaKanban && typeof metaKanban === "object" ? metaKanban["boardId"] : void 0;
|
|
697
|
+
const activeBoardId = context.currentKanbanBoardId ?? (typeof metaBoardId === "string" ? metaBoardId : void 0);
|
|
698
|
+
if (!activeBoardId || board.id !== activeBoardId || board.lifecycle?.mode !== "managed") {
|
|
699
|
+
return [...context.todos];
|
|
700
|
+
}
|
|
701
|
+
const projectedTodos = board.tasks.filter(
|
|
702
|
+
(task) => task.status !== "archived" && task.mergedIntoTaskId === void 0 && (!task.childTaskIds || task.childTaskIds.length === 0)
|
|
703
|
+
).sort(
|
|
704
|
+
(left, right) => left.createdAt.localeCompare(right.createdAt) || left.order - right.order
|
|
705
|
+
).map((task) => managedTodoFromTask(task, board.id));
|
|
706
|
+
if (sameTodos(context.todos, projectedTodos)) return [...context.todos];
|
|
707
|
+
suppressedTodoMirrors.add(context);
|
|
708
|
+
try {
|
|
709
|
+
context.state.replaceTodos(projectedTodos);
|
|
710
|
+
} finally {
|
|
711
|
+
suppressedTodoMirrors.delete(context);
|
|
712
|
+
}
|
|
713
|
+
notifyTodoUpdate(context, context.todos);
|
|
714
|
+
broadcastTodoUpdate(context, context.todos);
|
|
715
|
+
return [...context.todos];
|
|
716
|
+
}
|
|
625
717
|
async function applySessionKanbanTaskToSource(context, task, options = {}) {
|
|
626
718
|
const originId = task.origin?.taskId;
|
|
627
719
|
const graphId = task.origin?.graphId ?? "";
|
|
@@ -682,10 +774,12 @@ async function applySessionKanbanTaskToSource(context, task, options = {}) {
|
|
|
682
774
|
}
|
|
683
775
|
export {
|
|
684
776
|
SESSION_KANBAN_COLUMNS,
|
|
777
|
+
applyManagedKanbanBoardToTodos,
|
|
685
778
|
applySessionKanbanBoardToTodos,
|
|
686
779
|
applySessionKanbanTaskToSource,
|
|
687
780
|
attachSessionKanbanMirror,
|
|
688
781
|
cleanupEmptySessionKanbanBoards,
|
|
782
|
+
cleanupSessionKanbanBoard,
|
|
689
783
|
cleanupSessionKanbanBoardIfEmpty,
|
|
690
784
|
ensureSessionKanbanBoard,
|
|
691
785
|
hydrateSessionKanban,
|
package/dist/task.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import type { TaskStatus } from '@wrongstack/core/types';
|
|
1
|
+
import type { TaskStatus, Tool } from '@wrongstack/core/types';
|
|
2
2
|
import { type TaskItem } from '@wrongstack/core/utils';
|
|
3
|
-
|
|
3
|
+
type TaskReplacementItem = Omit<TaskItem, 'createdAt' | 'updatedAt'> & Partial<Pick<TaskItem, 'createdAt' | 'updatedAt'>>;
|
|
4
|
+
type TaskAdditionItem = Omit<TaskItem, 'id' | 'createdAt' | 'updatedAt' | 'status'> & Partial<Pick<TaskItem, 'status'>>;
|
|
4
5
|
interface TaskInput {
|
|
5
6
|
/** Replace: set new task list. Add: append a task. Status: update task status. Promote: convert a task to todo items. */
|
|
6
7
|
action: 'replace' | 'add' | 'status' | 'show' | 'promote' | 'planify';
|
|
7
8
|
/** Full task list for action=replace. */
|
|
8
|
-
tasks?:
|
|
9
|
+
tasks?: TaskReplacementItem[] | undefined;
|
|
9
10
|
/** Single task for action=add. id, createdAt, updatedAt are auto-generated. */
|
|
10
|
-
task?:
|
|
11
|
+
task?: TaskAdditionItem | undefined;
|
|
11
12
|
/** Task id for action=status or target for action=promote. */
|
|
12
13
|
id?: string | undefined;
|
|
13
14
|
/** New status for action=status. */
|