trekoon 0.3.6 → 0.3.8

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.
@@ -1,11 +1,7 @@
1
1
  import { TrackerDomain } from "../domain/tracker-domain";
2
2
  import { type DependencyRecord, type EpicRecord, type SubtaskRecord, type TaskRecord } from "../domain/types";
3
3
 
4
- interface SearchFields {
5
- readonly title: string;
6
- readonly description: string;
7
- readonly text: string;
8
- }
4
+ type BoardStatus = "todo" | "blocked" | "in_progress" | "done";
9
5
 
10
6
  interface StatusCounts {
11
7
  readonly total: number;
@@ -16,65 +12,63 @@ interface StatusCounts {
16
12
  readonly other: number;
17
13
  }
18
14
 
19
- export interface BoardSnapshotEpic {
15
+ interface FlatCounts extends Record<BoardStatus, number> {}
16
+
17
+ export interface BoardSnapshotDependency {
20
18
  readonly id: string;
21
- readonly title: string;
22
- readonly description: string;
23
- readonly status: string;
19
+ readonly sourceId: string;
20
+ readonly sourceKind: "task" | "subtask";
21
+ readonly dependsOnId: string;
22
+ readonly dependsOnKind: "task" | "subtask";
24
23
  readonly createdAt: number;
25
24
  readonly updatedAt: number;
26
- readonly taskIds: readonly string[];
27
- readonly counts: {
28
- readonly tasks: StatusCounts;
29
- readonly subtasks: StatusCounts;
30
- };
31
- readonly search: SearchFields;
32
25
  }
33
26
 
34
- export interface BoardSnapshotTask {
27
+ interface BoardSnapshotSubtask {
35
28
  readonly id: string;
36
- readonly epicId: string;
29
+ readonly kind: "subtask";
30
+ readonly taskId: string;
37
31
  readonly title: string;
38
32
  readonly description: string;
39
33
  readonly status: string;
34
+ readonly owner: string | null;
40
35
  readonly createdAt: number;
41
36
  readonly updatedAt: number;
42
- readonly subtaskIds: readonly string[];
37
+ readonly blockedBy: readonly string[];
38
+ readonly blocks: readonly string[];
43
39
  readonly dependencyIds: readonly string[];
44
40
  readonly dependentIds: readonly string[];
45
- readonly counts: {
46
- readonly subtasks: StatusCounts;
47
- readonly dependencies: number;
48
- readonly dependents: number;
49
- };
50
- readonly search: SearchFields;
41
+ readonly searchText: string;
51
42
  }
52
43
 
53
- export interface BoardSnapshotSubtask {
44
+ interface BoardSnapshotTask {
54
45
  readonly id: string;
55
- readonly taskId: string;
46
+ readonly kind: "task";
47
+ readonly epicId: string;
56
48
  readonly title: string;
57
49
  readonly description: string;
58
50
  readonly status: string;
51
+ readonly owner: string | null;
59
52
  readonly createdAt: number;
60
53
  readonly updatedAt: number;
54
+ readonly blockedBy: readonly string[];
55
+ readonly blocks: readonly string[];
61
56
  readonly dependencyIds: readonly string[];
62
57
  readonly dependentIds: readonly string[];
63
- readonly counts: {
64
- readonly dependencies: number;
65
- readonly dependents: number;
66
- };
67
- readonly search: SearchFields;
58
+ readonly subtasks: readonly BoardSnapshotSubtask[];
59
+ readonly searchText: string;
68
60
  }
69
61
 
70
- export interface BoardSnapshotDependency {
62
+ interface BoardSnapshotEpic {
71
63
  readonly id: string;
72
- readonly sourceId: string;
73
- readonly sourceKind: "task" | "subtask";
74
- readonly dependsOnId: string;
75
- readonly dependsOnKind: "task" | "subtask";
64
+ readonly title: string;
65
+ readonly description: string;
66
+ readonly status: string;
76
67
  readonly createdAt: number;
77
68
  readonly updatedAt: number;
69
+ readonly taskIds: readonly string[];
70
+ readonly counts: FlatCounts;
71
+ readonly searchText: string;
78
72
  }
79
73
 
80
74
  export interface BoardSnapshot {
@@ -91,57 +85,41 @@ export interface BoardSnapshot {
91
85
  };
92
86
  }
93
87
 
94
- function normalizeStatusBucket(status: string): keyof Omit<StatusCounts, "total"> {
95
- if (status === "todo") {
96
- return "todo";
97
- }
98
-
99
- if (status === "blocked") {
100
- return "blocked";
101
- }
102
-
103
- if (status === "in_progress" || status === "in-progress") {
104
- return "inProgress";
105
- }
106
-
107
- if (status === "done") {
108
- return "done";
109
- }
88
+ interface SnapshotDeltaSelection {
89
+ readonly epicIds?: readonly string[];
90
+ readonly taskIds?: readonly string[];
91
+ readonly subtaskIds?: readonly string[];
92
+ readonly dependencyIds?: readonly string[];
93
+ readonly deletedSubtaskIds?: readonly string[];
94
+ readonly deletedDependencyIds?: readonly string[];
95
+ }
110
96
 
97
+ function normalizeStatusBucket(status: string): keyof Omit<StatusCounts, "total"> {
98
+ if (status === "todo") return "todo";
99
+ if (status === "blocked") return "blocked";
100
+ if (status === "in_progress" || status === "in-progress") return "inProgress";
101
+ if (status === "done") return "done";
111
102
  return "other";
112
103
  }
113
104
 
114
105
  function countStatuses(records: readonly { readonly status: string }[]): StatusCounts {
115
- const counts: {
116
- total: number;
117
- todo: number;
118
- blocked: number;
119
- inProgress: number;
120
- done: number;
121
- other: number;
122
- } = {
123
- total: records.length,
124
- todo: 0,
125
- blocked: 0,
126
- inProgress: 0,
127
- done: 0,
128
- other: 0,
129
- };
130
-
106
+ const counts = { total: records.length, todo: 0, blocked: 0, inProgress: 0, done: 0, other: 0 };
131
107
  for (const record of records) {
132
- const bucket = normalizeStatusBucket(record.status);
133
- counts[bucket] += 1;
108
+ counts[normalizeStatusBucket(record.status)] += 1;
134
109
  }
135
-
136
110
  return counts;
137
111
  }
138
112
 
139
- function buildSearchFields(title: string, description: string): SearchFields {
140
- return {
141
- title,
142
- description,
143
- text: `${title}\n${description}`.trim(),
144
- };
113
+ function deriveFlatCounts(records: readonly { readonly status: string }[]): FlatCounts {
114
+ return records.reduce<FlatCounts>(
115
+ (counts, record) => {
116
+ if (record.status === "todo" || record.status === "blocked" || record.status === "in_progress" || record.status === "done") {
117
+ counts[record.status] += 1;
118
+ }
119
+ return counts;
120
+ },
121
+ { todo: 0, blocked: 0, in_progress: 0, done: 0 },
122
+ );
145
123
  }
146
124
 
147
125
  function mapDependency(record: DependencyRecord): BoardSnapshotDependency {
@@ -156,122 +134,176 @@ function mapDependency(record: DependencyRecord): BoardSnapshotDependency {
156
134
  };
157
135
  }
158
136
 
159
- export function buildBoardSnapshot(domain: TrackerDomain): BoardSnapshot {
160
- const generatedAt: number = Date.now();
161
- const epics: readonly EpicRecord[] = domain.listEpics();
162
- const tasks: readonly TaskRecord[] = domain.listTasks();
163
- const subtasks: readonly SubtaskRecord[] = domain.listSubtasks();
137
+ function uniqueIds(ids: readonly string[]): string[] {
138
+ return [...new Set(ids.filter((id) => id.length > 0))];
139
+ }
140
+
141
+ function buildDependencyIndexes(dependenciesBySourceId: Map<string, readonly DependencyRecord[]>, sourceIds: readonly string[]) {
142
+ const dependencyIdsBySource = new Map<string, string[]>();
143
+ const blockedByIdsBySource = new Map<string, string[]>();
144
+ const dependentIdsByTarget = new Map<string, string[]>();
145
+ const blocksByTarget = new Map<string, string[]>();
164
146
  const dependencies: BoardSnapshotDependency[] = [];
165
147
 
166
- const tasksByEpic = new Map<string, TaskRecord[]>();
148
+ for (const sourceId of sourceIds) {
149
+ for (const dependency of dependenciesBySourceId.get(sourceId) ?? []) {
150
+ dependencies.push(mapDependency(dependency));
151
+ (dependencyIdsBySource.get(dependency.sourceId) ?? dependencyIdsBySource.set(dependency.sourceId, []).get(dependency.sourceId) ?? []).push(dependency.id);
152
+ (blockedByIdsBySource.get(dependency.sourceId) ?? blockedByIdsBySource.set(dependency.sourceId, []).get(dependency.sourceId) ?? []).push(dependency.dependsOnId);
153
+ (dependentIdsByTarget.get(dependency.dependsOnId) ?? dependentIdsByTarget.set(dependency.dependsOnId, []).get(dependency.dependsOnId) ?? []).push(dependency.id);
154
+ (blocksByTarget.get(dependency.dependsOnId) ?? blocksByTarget.set(dependency.dependsOnId, []).get(dependency.dependsOnId) ?? []).push(dependency.sourceId);
155
+ }
156
+ }
157
+
158
+ return { dependencies, dependencyIdsBySource, blockedByIdsBySource, dependentIdsByTarget, blocksByTarget };
159
+ }
160
+
161
+ function mapSnapshotSubtask(subtask: SubtaskRecord, indexes: ReturnType<typeof buildDependencyIndexes>): BoardSnapshotSubtask {
162
+ return {
163
+ id: subtask.id,
164
+ kind: "subtask",
165
+ taskId: subtask.taskId,
166
+ title: subtask.title,
167
+ description: subtask.description,
168
+ status: subtask.status,
169
+ owner: subtask.owner ?? null,
170
+ createdAt: subtask.createdAt,
171
+ updatedAt: subtask.updatedAt,
172
+ blockedBy: indexes.blockedByIdsBySource.get(subtask.id) ?? [],
173
+ blocks: indexes.blocksByTarget.get(subtask.id) ?? [],
174
+ dependencyIds: indexes.dependencyIdsBySource.get(subtask.id) ?? [],
175
+ dependentIds: indexes.dependentIdsByTarget.get(subtask.id) ?? [],
176
+ searchText: [subtask.title, subtask.description, subtask.status].join(" ").toLowerCase(),
177
+ };
178
+ }
179
+
180
+ function mapSnapshotTask(task: TaskRecord, taskSubtasks: readonly BoardSnapshotSubtask[], indexes: ReturnType<typeof buildDependencyIndexes>): BoardSnapshotTask {
181
+ return {
182
+ id: task.id,
183
+ kind: "task",
184
+ epicId: task.epicId,
185
+ title: task.title,
186
+ description: task.description,
187
+ status: task.status,
188
+ owner: task.owner ?? null,
189
+ createdAt: task.createdAt,
190
+ updatedAt: task.updatedAt,
191
+ blockedBy: indexes.blockedByIdsBySource.get(task.id) ?? [],
192
+ blocks: indexes.blocksByTarget.get(task.id) ?? [],
193
+ dependencyIds: indexes.dependencyIdsBySource.get(task.id) ?? [],
194
+ dependentIds: indexes.dependentIdsByTarget.get(task.id) ?? [],
195
+ subtasks: taskSubtasks,
196
+ searchText: [task.title, task.description, task.status, ...taskSubtasks.map((subtask) => `${subtask.title} ${subtask.description} ${subtask.status}`)].join(" ").toLowerCase(),
197
+ };
198
+ }
199
+
200
+ function mapSnapshotEpic(epic: EpicRecord, epicTasks: readonly BoardSnapshotTask[]): BoardSnapshotEpic {
201
+ return {
202
+ id: epic.id,
203
+ title: epic.title,
204
+ description: epic.description,
205
+ status: epic.status,
206
+ createdAt: epic.createdAt,
207
+ updatedAt: epic.updatedAt,
208
+ taskIds: epicTasks.map((task) => task.id),
209
+ counts: deriveFlatCounts(epicTasks),
210
+ searchText: [epic.title, epic.description, ...epicTasks.map((task) => task.searchText)].join(" ").toLowerCase(),
211
+ };
212
+ }
213
+
214
+ export function buildBoardSnapshotDelta(domain: TrackerDomain, selection: SnapshotDeltaSelection): Record<string, unknown> {
215
+ const epicIds = uniqueIds(selection.epicIds ?? []);
216
+ const requestedTaskIds = uniqueIds(selection.taskIds ?? []);
217
+ const requestedSubtaskIds = uniqueIds(selection.subtaskIds ?? []);
218
+ const requestedDependencyIds = new Set(selection.dependencyIds ?? []);
219
+ const relatedTaskIds = uniqueIds([
220
+ ...requestedTaskIds,
221
+ ...requestedSubtaskIds.map((subtaskId) => domain.getSubtask(subtaskId)?.taskId ?? ""),
222
+ ]);
223
+ const tasks = relatedTaskIds.map((taskId) => domain.getTask(taskId)).filter((task): task is TaskRecord => task !== null);
224
+ const subtasksByTaskId = new Map<string, readonly SubtaskRecord[]>();
225
+ for (const task of tasks) {
226
+ subtasksByTaskId.set(task.id, domain.listSubtasks(task.id));
227
+ }
228
+ const allSubtasks = uniqueIds([
229
+ ...requestedSubtaskIds,
230
+ ...[...subtasksByTaskId.values()].flatMap((taskSubtasks) => taskSubtasks.map((subtask) => subtask.id)),
231
+ ]).map((subtaskId) => domain.getSubtask(subtaskId)).filter((subtask): subtask is SubtaskRecord => subtask !== null);
232
+ const sourceIds = uniqueIds([...tasks.map((task) => task.id), ...allSubtasks.map((subtask) => subtask.id)]);
233
+ const indexes = buildDependencyIndexes(domain.listDependenciesBySourceIds(sourceIds), sourceIds);
234
+ const snapshotSubtasksByTaskId = new Map<string, BoardSnapshotSubtask[]>();
235
+ for (const subtask of allSubtasks) {
236
+ const mappedSubtask = mapSnapshotSubtask(subtask, indexes);
237
+ const taskSubtasks = snapshotSubtasksByTaskId.get(subtask.taskId) ?? [];
238
+ taskSubtasks.push(mappedSubtask);
239
+ snapshotSubtasksByTaskId.set(subtask.taskId, taskSubtasks);
240
+ }
241
+ const snapshotTasks = tasks.map((task) => mapSnapshotTask(task, snapshotSubtasksByTaskId.get(task.id) ?? [], indexes));
242
+ const snapshotEpics = epicIds.map((epicId) => domain.getEpic(epicId)).filter((epic): epic is EpicRecord => epic !== null).map((epic) => mapSnapshotEpic(epic, snapshotTasks.filter((task) => task.epicId === epic.id)));
243
+
244
+ return {
245
+ generatedAt: Date.now(),
246
+ epics: snapshotEpics,
247
+ tasks: snapshotTasks.filter((task) => requestedTaskIds.includes(task.id)),
248
+ subtasks: allSubtasks.map((subtask) => mapSnapshotSubtask(subtask, indexes)).filter((subtask) => requestedSubtaskIds.includes(subtask.id)),
249
+ dependencies: indexes.dependencies.filter((dependency) => requestedDependencyIds.has(dependency.id)),
250
+ deletedSubtaskIds: [...(selection.deletedSubtaskIds ?? [])],
251
+ deletedDependencyIds: [...(selection.deletedDependencyIds ?? [])],
252
+ };
253
+ }
254
+
255
+ export function buildBoardSnapshot(domain: TrackerDomain): BoardSnapshot {
256
+ const generatedAt = Date.now();
257
+ const epics = domain.listEpics();
258
+ const tasks = domain.listTasks();
259
+ const subtasks = domain.listSubtasks();
260
+ const sourceIds = [...tasks.map((task) => task.id), ...subtasks.map((subtask) => subtask.id)];
261
+ const dependenciesBySourceId = domain.listDependenciesBySourceIds(sourceIds);
262
+ const subtasksByTaskId = new Map<string, SubtaskRecord[]>();
263
+ const tasksByEpicId = new Map<string, TaskRecord[]>();
264
+ const indexes = buildDependencyIndexes(dependenciesBySourceId, sourceIds);
265
+
167
266
  for (const task of tasks) {
168
- const existing = tasksByEpic.get(task.epicId) ?? [];
267
+ const existing = tasksByEpicId.get(task.epicId) ?? [];
169
268
  existing.push(task);
170
- tasksByEpic.set(task.epicId, existing);
269
+ tasksByEpicId.set(task.epicId, existing);
171
270
  }
172
271
 
173
- const subtasksByTask = new Map<string, SubtaskRecord[]>();
174
272
  for (const subtask of subtasks) {
175
- const existing = subtasksByTask.get(subtask.taskId) ?? [];
273
+ const existing = subtasksByTaskId.get(subtask.taskId) ?? [];
176
274
  existing.push(subtask);
177
- subtasksByTask.set(subtask.taskId, existing);
275
+ subtasksByTaskId.set(subtask.taskId, existing);
178
276
  }
179
277
 
180
- const dependencyIdsBySource = new Map<string, string[]>();
181
- const dependentIdsByTarget = new Map<string, string[]>();
182
- for (const task of tasks) {
183
- for (const dependency of domain.listDependencies(task.id)) {
184
- dependencies.push(mapDependency(dependency));
185
- const sourceIds = dependencyIdsBySource.get(dependency.sourceId) ?? [];
186
- sourceIds.push(dependency.id);
187
- dependencyIdsBySource.set(dependency.sourceId, sourceIds);
188
- const dependentIds = dependentIdsByTarget.get(dependency.dependsOnId) ?? [];
189
- dependentIds.push(dependency.id);
190
- dependentIdsByTarget.set(dependency.dependsOnId, dependentIds);
191
- }
278
+ const snapshotSubtasks: BoardSnapshotSubtask[] = subtasks.map((subtask) => mapSnapshotSubtask(subtask, indexes));
279
+ const snapshotSubtasksByTaskId = new Map<string, BoardSnapshotSubtask[]>();
280
+ for (const subtask of snapshotSubtasks) {
281
+ const existing = snapshotSubtasksByTaskId.get(subtask.taskId) ?? [];
282
+ existing.push(subtask);
283
+ snapshotSubtasksByTaskId.set(subtask.taskId, existing);
192
284
  }
193
285
 
194
- for (const subtask of subtasks) {
195
- for (const dependency of domain.listDependencies(subtask.id)) {
196
- dependencies.push(mapDependency(dependency));
197
- const sourceIds = dependencyIdsBySource.get(dependency.sourceId) ?? [];
198
- sourceIds.push(dependency.id);
199
- dependencyIdsBySource.set(dependency.sourceId, sourceIds);
200
- const dependentIds = dependentIdsByTarget.get(dependency.dependsOnId) ?? [];
201
- dependentIds.push(dependency.id);
202
- dependentIdsByTarget.set(dependency.dependsOnId, dependentIds);
203
- }
286
+ const snapshotTasks: BoardSnapshotTask[] = tasks.map((task) => mapSnapshotTask(task, snapshotSubtasksByTaskId.get(task.id) ?? [], indexes));
287
+ const taskSearchTextByEpicId = new Map<string, string[]>();
288
+ for (const task of snapshotTasks) {
289
+ const existing = taskSearchTextByEpicId.get(task.epicId) ?? [];
290
+ existing.push(task.searchText);
291
+ taskSearchTextByEpicId.set(task.epicId, existing);
204
292
  }
205
293
 
294
+ const snapshotEpics: BoardSnapshotEpic[] = epics.map((epic) => mapSnapshotEpic(epic, snapshotTasks.filter((task) => task.epicId === epic.id)));
295
+
206
296
  return {
207
297
  generatedAt,
208
- epics: epics.map((epic) => {
209
- const epicTasks = tasksByEpic.get(epic.id) ?? [];
210
- const epicSubtasks = epicTasks.flatMap((task) => subtasksByTask.get(task.id) ?? []);
211
- return {
212
- id: epic.id,
213
- title: epic.title,
214
- description: epic.description,
215
- status: epic.status,
216
- createdAt: epic.createdAt,
217
- updatedAt: epic.updatedAt,
218
- taskIds: epicTasks.map((task) => task.id),
219
- counts: {
220
- tasks: countStatuses(epicTasks),
221
- subtasks: countStatuses(epicSubtasks),
222
- },
223
- search: buildSearchFields(epic.title, epic.description),
224
- };
225
- }),
226
- tasks: tasks.map((task) => {
227
- const taskSubtasks = subtasksByTask.get(task.id) ?? [];
228
- const dependencyIds = dependencyIdsBySource.get(task.id) ?? [];
229
- const dependentIds = dependentIdsByTarget.get(task.id) ?? [];
230
- return {
231
- id: task.id,
232
- epicId: task.epicId,
233
- title: task.title,
234
- description: task.description,
235
- status: task.status,
236
- createdAt: task.createdAt,
237
- updatedAt: task.updatedAt,
238
- subtaskIds: taskSubtasks.map((subtask) => subtask.id),
239
- dependencyIds,
240
- dependentIds,
241
- counts: {
242
- subtasks: countStatuses(taskSubtasks),
243
- dependencies: dependencyIds.length,
244
- dependents: dependentIds.length,
245
- },
246
- search: buildSearchFields(task.title, task.description),
247
- };
248
- }),
249
- subtasks: subtasks.map((subtask) => {
250
- const dependencyIds = dependencyIdsBySource.get(subtask.id) ?? [];
251
- const dependentIds = dependentIdsByTarget.get(subtask.id) ?? [];
252
- return {
253
- id: subtask.id,
254
- taskId: subtask.taskId,
255
- title: subtask.title,
256
- description: subtask.description,
257
- status: subtask.status,
258
- createdAt: subtask.createdAt,
259
- updatedAt: subtask.updatedAt,
260
- dependencyIds,
261
- dependentIds,
262
- counts: {
263
- dependencies: dependencyIds.length,
264
- dependents: dependentIds.length,
265
- },
266
- search: buildSearchFields(subtask.title, subtask.description),
267
- };
268
- }),
269
- dependencies,
298
+ epics: snapshotEpics,
299
+ tasks: snapshotTasks,
300
+ subtasks: snapshotSubtasks,
301
+ dependencies: indexes.dependencies,
270
302
  counts: {
271
303
  epics: countStatuses(epics),
272
304
  tasks: countStatuses(tasks),
273
305
  subtasks: countStatuses(subtasks),
274
- dependencies: dependencies.length,
306
+ dependencies: indexes.dependencies.length,
275
307
  },
276
308
  };
277
309
  }
@@ -121,7 +121,7 @@ export async function runBoard(context: CliContext): Promise<CliResult> {
121
121
  return okResult({
122
122
  command: "board.open",
123
123
  human: [
124
- `Board ready at ${server.url}`,
124
+ `Board ready at ${server.fallbackUrl}`,
125
125
  launch.launched
126
126
  ? `Browser launched with ${launch.command}`
127
127
  : `Browser launch failed: ${launch.errorMessage ?? "unknown failure"}`,
@@ -73,17 +73,23 @@ export async function runEvents(context: CliContext): Promise<CliResult> {
73
73
  archive,
74
74
  });
75
75
 
76
- return okResult({
77
- command: "events.prune",
78
- human: [
79
- dryRun ? "Dry run complete." : "Prune complete.",
80
- `Retention days: ${summary.retentionDays}`,
81
- `Candidates: ${summary.candidateCount}`,
82
- `Archived: ${summary.archivedCount}`,
83
- `Deleted: ${summary.deletedCount}`,
84
- ].join("\n"),
85
- data: summary,
86
- });
76
+ return okResult({
77
+ command: "events.prune",
78
+ human: [
79
+ dryRun ? "Dry run complete." : "Prune complete.",
80
+ `Retention days: ${summary.retentionDays}`,
81
+ `Candidates: ${summary.candidateCount}`,
82
+ `Archived: ${summary.archivedCount}`,
83
+ `Deleted: ${summary.deletedCount}`,
84
+ summary.staleCursorCount > 0
85
+ ? `Sync guidance: ${summary.staleCursorCount} cursor(s) reference pruned history. Run 'trekoon sync pull --from <branch>' and rebuild if stale cursor hints persist.`
86
+ : "Sync guidance: pruning stayed within retained cursor history.",
87
+ archive
88
+ ? "Retention automation: archived copies were kept before deletion."
89
+ : "Retention automation: rerun with --archive to keep retained copies before deletion.",
90
+ ].join("\n"),
91
+ data: summary,
92
+ });
87
93
  } catch (error: unknown) {
88
94
  const busyFailure = sqliteBusyFailure("events.prune", error);
89
95
  if (busyFailure !== null) {
@@ -4,6 +4,16 @@ import { type CliContext, type CliResult } from "../runtime/command-types";
4
4
  const QUICKSTART_TEXT = [
5
5
  "Trekoon quickstart",
6
6
  "",
7
+ "Human workflow:",
8
+ " 1. Gather context through discussion, brainstorming, or research.",
9
+ " 2. Run: trekoon plan <goal>",
10
+ " Use this when you want Trekoon to turn the goal into an execution-ready epic.",
11
+ " 3. Run: trekoon <epic-id>",
12
+ " Use this to inspect the epic, next ready work, and blockers before execution.",
13
+ " 4. Run: trekoon <epic-id> execute",
14
+ " Use this when you want the agent to keep working until the epic is done,",
15
+ " all remaining work is blocked, or it needs your input.",
16
+ "",
7
17
  "Agents: always use --toon on every command.",
8
18
  "Aligned with: .agents/skills/trekoon/SKILL.md",
9
19
  "",
@@ -935,12 +935,12 @@ export async function runSubtask(context: CliContext): Promise<CliResult> {
935
935
  }
936
936
  case "delete": {
937
937
  const subtaskId: string = parsed.positional[1] ?? "";
938
- mutations.deleteSubtask(subtaskId);
938
+ const result = mutations.deleteSubtask(subtaskId);
939
939
 
940
940
  return okResult({
941
941
  command: "subtask.delete",
942
942
  human: `Deleted subtask ${subtaskId}`,
943
- data: { id: subtaskId },
943
+ data: { id: subtaskId, deletedDependencyIds: result.deletedDependencyIds },
944
944
  });
945
945
  }
946
946
  default: