clawvault 2.4.6 → 2.4.7

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 (69) hide show
  1. package/bin/clawvault.js +5 -0
  2. package/bin/command-registration.test.js +1 -1
  3. package/bin/help-contract.test.js +1 -0
  4. package/bin/register-config-route-commands.test.js +8 -1
  5. package/bin/register-core-commands.js +3 -3
  6. package/bin/register-project-commands.js +209 -0
  7. package/bin/register-project-commands.test.js +201 -0
  8. package/bin/register-query-commands.js +40 -0
  9. package/bin/register-task-commands.js +2 -18
  10. package/bin/register-task-commands.test.js +3 -4
  11. package/bin/test-helpers/cli-command-fixtures.js +5 -0
  12. package/dist/{chunk-3PJIGGWV.js → chunk-2CDEETQN.js} +1 -0
  13. package/dist/{chunk-FD2ZA65C.js → chunk-2RK2AG32.js} +5 -5
  14. package/dist/chunk-5GZFTAL7.js +340 -0
  15. package/dist/{chunk-P2ZH6AN5.js → chunk-6RQPD7X6.js} +3 -4
  16. package/dist/{chunk-HNMFXFYP.js → chunk-7OHQFMJK.js} +2 -1
  17. package/dist/{chunk-FKQJB6XC.js → chunk-C3PF7WBA.js} +2 -2
  18. package/dist/{chunk-JXY6T5R7.js → chunk-FW465EEA.js} +1 -1
  19. package/dist/{chunk-BI6SGGZP.js → chunk-G3OQJ2NQ.js} +1 -1
  20. package/dist/chunk-GSD4ALSI.js +724 -0
  21. package/dist/{chunk-6QLRSPLZ.js → chunk-IOALNTAN.js} +268 -47
  22. package/dist/chunk-ITPEXLHA.js +528 -0
  23. package/dist/{chunk-LLN5SPGL.js → chunk-J5EMBUPK.js} +1 -1
  24. package/dist/chunk-K3CDT7IH.js +122 -0
  25. package/dist/{chunk-AHGUJG76.js → chunk-KCCHROBR.js} +13 -69
  26. package/dist/{chunk-JTO7NZLS.js → chunk-LMCC5OC7.js} +2 -2
  27. package/dist/{chunk-QALB2V3E.js → chunk-MQUJNOHK.js} +1 -1
  28. package/dist/{chunk-H6WQUUNK.js → chunk-TMZMN7OS.js} +334 -457
  29. package/dist/{chunk-HVTTYDCJ.js → chunk-VR5NE7PZ.js} +1 -1
  30. package/dist/{chunk-22WE3J4F.js → chunk-WIICLBNF.js} +35 -4
  31. package/dist/chunk-YCVDVI5B.js +273 -0
  32. package/dist/{chunk-NAMFB7ZA.js → chunk-Z2XBWN7A.js} +0 -2
  33. package/dist/commands/archive.js +3 -3
  34. package/dist/commands/backlog.js +1 -1
  35. package/dist/commands/blocked.js +1 -1
  36. package/dist/commands/canvas.d.ts +1 -14
  37. package/dist/commands/canvas.js +123 -1543
  38. package/dist/commands/context.js +5 -6
  39. package/dist/commands/doctor.js +2 -2
  40. package/dist/commands/inject.d.ts +2 -0
  41. package/dist/commands/inject.js +14 -0
  42. package/dist/commands/kanban.js +2 -2
  43. package/dist/commands/migrate-observations.js +2 -2
  44. package/dist/commands/observe.js +8 -6
  45. package/dist/commands/project.d.ts +85 -0
  46. package/dist/commands/project.js +411 -0
  47. package/dist/commands/rebuild.js +7 -5
  48. package/dist/commands/reflect.js +5 -4
  49. package/dist/commands/replay.js +10 -7
  50. package/dist/commands/setup.d.ts +1 -1
  51. package/dist/commands/setup.js +2 -2
  52. package/dist/commands/sleep.d.ts +1 -1
  53. package/dist/commands/sleep.js +11 -8
  54. package/dist/commands/status.js +2 -2
  55. package/dist/commands/task.d.ts +2 -2
  56. package/dist/commands/task.js +11 -301
  57. package/dist/commands/wake.d.ts +1 -1
  58. package/dist/commands/wake.js +4 -4
  59. package/dist/index.d.ts +75 -107
  60. package/dist/index.js +78 -36
  61. package/dist/inject-x65KXWPk.d.ts +137 -0
  62. package/dist/lib/project-utils.d.ts +97 -0
  63. package/dist/lib/project-utils.js +19 -0
  64. package/dist/lib/task-utils.d.ts +8 -3
  65. package/dist/lib/task-utils.js +1 -1
  66. package/dist/{types-DMU3SuAV.d.ts → types-jjuYN2Xn.d.ts} +1 -1
  67. package/package.json +2 -2
  68. package/dist/chunk-L3DJ36BZ.js +0 -40
  69. package/dist/chunk-UMMCYTJV.js +0 -105
@@ -1,33 +1,182 @@
1
1
  // src/lib/task-utils.ts
2
+ import * as fs2 from "fs";
3
+ import * as path2 from "path";
4
+ import matter from "gray-matter";
5
+
6
+ // src/lib/transition-ledger.ts
2
7
  import * as fs from "fs";
3
8
  import * as path from "path";
4
- import matter from "gray-matter";
9
+ var REGRESSION_PAIRS = [
10
+ ["done", "open"],
11
+ ["done", "blocked"],
12
+ ["in-progress", "blocked"]
13
+ ];
14
+ function isRegression(from, to) {
15
+ return REGRESSION_PAIRS.some(([f, t]) => f === from && t === to);
16
+ }
17
+ function getLedgerDir(vaultPath) {
18
+ return path.join(path.resolve(vaultPath), "ledger", "transitions");
19
+ }
20
+ function getTodayLedgerPath(vaultPath) {
21
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
22
+ return path.join(getLedgerDir(vaultPath), `${date}.jsonl`);
23
+ }
24
+ var RETRYABLE_APPEND_CODES = /* @__PURE__ */ new Set(["ENOENT", "EAGAIN", "EBUSY"]);
25
+ var MAX_APPEND_RETRIES = 2;
26
+ function asErrno(error) {
27
+ if (!error || typeof error !== "object") {
28
+ return null;
29
+ }
30
+ return error;
31
+ }
32
+ function formatLedgerWriteError(filePath, error) {
33
+ const errno = asErrno(error);
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ if (errno?.code === "ENOSPC") {
36
+ return new Error(`Failed to write transition ledger at ${filePath}: no space left on device.`);
37
+ }
38
+ if (errno?.code === "EACCES" || errno?.code === "EPERM") {
39
+ return new Error(`Failed to write transition ledger at ${filePath}: permission denied.`);
40
+ }
41
+ return new Error(`Failed to write transition ledger at ${filePath}: ${message}`);
42
+ }
43
+ function appendTransition(vaultPath, event) {
44
+ const ledgerDir = getLedgerDir(vaultPath);
45
+ try {
46
+ fs.mkdirSync(ledgerDir, { recursive: true });
47
+ } catch (error) {
48
+ throw formatLedgerWriteError(ledgerDir, error);
49
+ }
50
+ const filePath = getTodayLedgerPath(vaultPath);
51
+ const payload = JSON.stringify(event) + "\n";
52
+ for (let attempt = 0; attempt <= MAX_APPEND_RETRIES; attempt += 1) {
53
+ try {
54
+ fs.appendFileSync(filePath, payload);
55
+ return;
56
+ } catch (error) {
57
+ const errno = asErrno(error);
58
+ const code = errno?.code;
59
+ if (code === "ENOENT") {
60
+ try {
61
+ fs.mkdirSync(ledgerDir, { recursive: true });
62
+ } catch (mkdirError) {
63
+ throw formatLedgerWriteError(filePath, mkdirError);
64
+ }
65
+ }
66
+ if (code && RETRYABLE_APPEND_CODES.has(code) && attempt < MAX_APPEND_RETRIES) {
67
+ continue;
68
+ }
69
+ throw formatLedgerWriteError(filePath, error);
70
+ }
71
+ }
72
+ }
73
+ function buildTransitionEvent(taskId, fromStatus, toStatus, options = {}) {
74
+ const agentId = process.env.OPENCLAW_AGENT_ID || "manual";
75
+ const costTokensRaw = process.env.OPENCLAW_TOKEN_ESTIMATE;
76
+ const costTokens = costTokensRaw ? parseInt(costTokensRaw, 10) : null;
77
+ return {
78
+ task_id: taskId,
79
+ agent_id: agentId,
80
+ from_status: fromStatus,
81
+ to_status: toStatus,
82
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
83
+ confidence: options.confidence ?? (agentId === "manual" ? 1 : 1),
84
+ cost_tokens: costTokens !== null && !isNaN(costTokens) ? costTokens : null,
85
+ reason: options.reason || null
86
+ };
87
+ }
88
+ function readAllTransitions(vaultPath) {
89
+ const ledgerDir = getLedgerDir(vaultPath);
90
+ if (!fs.existsSync(ledgerDir)) return [];
91
+ let files = [];
92
+ try {
93
+ files = fs.readdirSync(ledgerDir).filter((f) => f.endsWith(".jsonl")).sort();
94
+ } catch {
95
+ return [];
96
+ }
97
+ const events = [];
98
+ for (const file of files) {
99
+ let lines = [];
100
+ try {
101
+ lines = fs.readFileSync(path.join(ledgerDir, file), "utf-8").split("\n").filter((l) => l.trim());
102
+ } catch {
103
+ continue;
104
+ }
105
+ for (const line of lines) {
106
+ try {
107
+ events.push(JSON.parse(line));
108
+ } catch {
109
+ }
110
+ }
111
+ }
112
+ return events;
113
+ }
114
+ function queryTransitions(vaultPath, filters = {}) {
115
+ let events = readAllTransitions(vaultPath);
116
+ if (filters.taskId) {
117
+ events = events.filter((e) => e.task_id === filters.taskId);
118
+ }
119
+ if (filters.agent) {
120
+ events = events.filter((e) => e.agent_id === filters.agent);
121
+ }
122
+ if (filters.failed) {
123
+ events = events.filter((e) => isRegression(e.from_status, e.to_status));
124
+ }
125
+ return events;
126
+ }
127
+ function countBlockedTransitions(vaultPath, taskId) {
128
+ const events = readAllTransitions(vaultPath);
129
+ return events.filter((e) => e.task_id === taskId && e.to_status === "blocked").length;
130
+ }
131
+ function formatTransitionsTable(events) {
132
+ if (events.length === 0) return "No transitions found.\n";
133
+ const headers = ["TIMESTAMP", "TASK", "FROM\u2192TO", "AGENT", "REASON"];
134
+ const widths = [20, 20, 24, 16, 30];
135
+ let output = headers.map((h, i) => h.padEnd(widths[i])).join(" ") + "\n";
136
+ output += "-".repeat(widths.reduce((a, b) => a + b + 2, 0)) + "\n";
137
+ for (const e of events) {
138
+ const ts = e.timestamp.replace("T", " ").slice(0, 19);
139
+ const taskId = e.task_id.length > widths[1] ? e.task_id.slice(0, widths[1] - 3) + "..." : e.task_id;
140
+ const transition = `${e.from_status} \u2192 ${e.to_status}`;
141
+ const reason = e.reason ? e.reason.length > widths[4] ? e.reason.slice(0, widths[4] - 3) + "..." : e.reason : "-";
142
+ output += [
143
+ ts.padEnd(widths[0]),
144
+ taskId.padEnd(widths[1]),
145
+ transition.padEnd(widths[2]),
146
+ e.agent_id.padEnd(widths[3]),
147
+ reason
148
+ ].join(" ") + "\n";
149
+ }
150
+ return output;
151
+ }
152
+
153
+ // src/lib/task-utils.ts
5
154
  function slugify(text) {
6
155
  return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").trim();
7
156
  }
8
157
  function getTasksDir(vaultPath) {
9
- return path.join(path.resolve(vaultPath), "tasks");
158
+ return path2.join(path2.resolve(vaultPath), "tasks");
10
159
  }
11
160
  function getBacklogDir(vaultPath) {
12
- return path.join(path.resolve(vaultPath), "backlog");
161
+ return path2.join(path2.resolve(vaultPath), "backlog");
13
162
  }
14
163
  function ensureTasksDir(vaultPath) {
15
164
  const tasksDir = getTasksDir(vaultPath);
16
- if (!fs.existsSync(tasksDir)) {
17
- fs.mkdirSync(tasksDir, { recursive: true });
165
+ if (!fs2.existsSync(tasksDir)) {
166
+ fs2.mkdirSync(tasksDir, { recursive: true });
18
167
  }
19
168
  }
20
169
  function ensureBacklogDir(vaultPath) {
21
170
  const backlogDir = getBacklogDir(vaultPath);
22
- if (!fs.existsSync(backlogDir)) {
23
- fs.mkdirSync(backlogDir, { recursive: true });
171
+ if (!fs2.existsSync(backlogDir)) {
172
+ fs2.mkdirSync(backlogDir, { recursive: true });
24
173
  }
25
174
  }
26
175
  function getTaskPath(vaultPath, slug) {
27
- return path.join(getTasksDir(vaultPath), `${slug}.md`);
176
+ return path2.join(getTasksDir(vaultPath), `${slug}.md`);
28
177
  }
29
178
  function getBacklogPath(vaultPath, slug) {
30
- return path.join(getBacklogDir(vaultPath), `${slug}.md`);
179
+ return path2.join(getBacklogDir(vaultPath), `${slug}.md`);
31
180
  }
32
181
  function extractTitle(content) {
33
182
  const match = content.match(/^#\s+(.+)$/m);
@@ -43,13 +192,76 @@ function startOfToday() {
43
192
  const now = /* @__PURE__ */ new Date();
44
193
  return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
45
194
  }
195
+ var VALID_TASK_STATUSES = /* @__PURE__ */ new Set([
196
+ "open",
197
+ "in-progress",
198
+ "blocked",
199
+ "done"
200
+ ]);
201
+ function isTaskStatus(value) {
202
+ return typeof value === "string" && VALID_TASK_STATUSES.has(value);
203
+ }
204
+ function persistTaskFrontmatter(task, frontmatter) {
205
+ fs2.writeFileSync(task.path, matter.stringify(task.content, frontmatter));
206
+ }
207
+ function resolveStatusTransition(previousStatus, nextStatus) {
208
+ if (!isTaskStatus(previousStatus) || !isTaskStatus(nextStatus)) {
209
+ return null;
210
+ }
211
+ if (previousStatus === nextStatus) {
212
+ return null;
213
+ }
214
+ return { fromStatus: previousStatus, toStatus: nextStatus };
215
+ }
216
+ function logStatusTransition({
217
+ vaultPath,
218
+ task,
219
+ fromStatus,
220
+ toStatus,
221
+ frontmatter,
222
+ options
223
+ }) {
224
+ const normalizedReason = typeof options.reason === "string" ? options.reason.trim() : "";
225
+ const reason = normalizedReason || (isRegression(fromStatus, toStatus) ? `regression: ${fromStatus} -> ${toStatus}` : void 0);
226
+ const event = buildTransitionEvent(task.slug, fromStatus, toStatus, {
227
+ confidence: options.confidence,
228
+ reason
229
+ });
230
+ try {
231
+ appendTransition(vaultPath, event);
232
+ } catch {
233
+ return frontmatter;
234
+ }
235
+ if (toStatus !== "blocked" || frontmatter.escalation) {
236
+ return frontmatter;
237
+ }
238
+ let blockedCount = 0;
239
+ try {
240
+ blockedCount = countBlockedTransitions(vaultPath, task.slug);
241
+ } catch {
242
+ return frontmatter;
243
+ }
244
+ if (blockedCount < 3) {
245
+ return frontmatter;
246
+ }
247
+ const escalatedFrontmatter = {
248
+ ...frontmatter,
249
+ escalation: true
250
+ };
251
+ try {
252
+ persistTaskFrontmatter(task, escalatedFrontmatter);
253
+ return escalatedFrontmatter;
254
+ } catch {
255
+ return frontmatter;
256
+ }
257
+ }
46
258
  function readTask(vaultPath, slug) {
47
259
  const taskPath = getTaskPath(vaultPath, slug);
48
- if (!fs.existsSync(taskPath)) {
260
+ if (!fs2.existsSync(taskPath)) {
49
261
  return null;
50
262
  }
51
263
  try {
52
- const raw = fs.readFileSync(taskPath, "utf-8");
264
+ const raw = fs2.readFileSync(taskPath, "utf-8");
53
265
  const { data, content } = matter(raw);
54
266
  const title = extractTitle(content) || slug;
55
267
  return {
@@ -65,11 +277,11 @@ function readTask(vaultPath, slug) {
65
277
  }
66
278
  function readBacklogItem(vaultPath, slug) {
67
279
  const backlogPath = getBacklogPath(vaultPath, slug);
68
- if (!fs.existsSync(backlogPath)) {
280
+ if (!fs2.existsSync(backlogPath)) {
69
281
  return null;
70
282
  }
71
283
  try {
72
- const raw = fs.readFileSync(backlogPath, "utf-8");
284
+ const raw = fs2.readFileSync(backlogPath, "utf-8");
73
285
  const { data, content } = matter(raw);
74
286
  const title = extractTitle(content) || slug;
75
287
  return {
@@ -85,11 +297,11 @@ function readBacklogItem(vaultPath, slug) {
85
297
  }
86
298
  function listTasks(vaultPath, filters) {
87
299
  const tasksDir = getTasksDir(vaultPath);
88
- if (!fs.existsSync(tasksDir)) {
300
+ if (!fs2.existsSync(tasksDir)) {
89
301
  return [];
90
302
  }
91
303
  const tasks = [];
92
- const entries = fs.readdirSync(tasksDir, { withFileTypes: true });
304
+ const entries = fs2.readdirSync(tasksDir, { withFileTypes: true });
93
305
  const today = startOfToday();
94
306
  for (const entry of entries) {
95
307
  if (!entry.isFile() || !entry.name.endsWith(".md")) {
@@ -145,11 +357,11 @@ function listTasks(vaultPath, filters) {
145
357
  }
146
358
  function listBacklogItems(vaultPath, filters) {
147
359
  const backlogDir = getBacklogDir(vaultPath);
148
- if (!fs.existsSync(backlogDir)) {
360
+ if (!fs2.existsSync(backlogDir)) {
149
361
  return [];
150
362
  }
151
363
  const items = [];
152
- const entries = fs.readdirSync(backlogDir, { withFileTypes: true });
364
+ const entries = fs2.readdirSync(backlogDir, { withFileTypes: true });
153
365
  for (const entry of entries) {
154
366
  if (!entry.isFile() || !entry.name.endsWith(".md")) {
155
367
  continue;
@@ -171,7 +383,7 @@ function createTask(vaultPath, title, options = {}) {
171
383
  ensureTasksDir(vaultPath);
172
384
  const slug = slugify(title);
173
385
  const taskPath = getTaskPath(vaultPath, slug);
174
- if (fs.existsSync(taskPath)) {
386
+ if (fs2.existsSync(taskPath)) {
175
387
  throw new Error(`Task already exists: ${slug}`);
176
388
  }
177
389
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -206,7 +418,7 @@ ${options.content}
206
418
  `;
207
419
  }
208
420
  const fileContent = matter.stringify(content, frontmatter);
209
- fs.writeFileSync(taskPath, fileContent);
421
+ fs2.writeFileSync(taskPath, fileContent);
210
422
  return {
211
423
  slug,
212
424
  title,
@@ -215,13 +427,17 @@ ${options.content}
215
427
  path: taskPath
216
428
  };
217
429
  }
218
- function updateTask(vaultPath, slug, updates) {
430
+ function updateTask(vaultPath, slug, updates, options = {}) {
219
431
  const task = readTask(vaultPath, slug);
220
432
  if (!task) {
221
433
  throw new Error(`Task not found: ${slug}`);
222
434
  }
435
+ if (updates.status !== void 0 && !isTaskStatus(updates.status)) {
436
+ throw new Error(`Invalid task status: ${String(updates.status)}`);
437
+ }
438
+ const previousStatus = task.frontmatter.status;
223
439
  const now = (/* @__PURE__ */ new Date()).toISOString();
224
- const newFrontmatter = {
440
+ let newFrontmatter = {
225
441
  ...task.frontmatter,
226
442
  updated: now
227
443
  };
@@ -348,41 +564,39 @@ function updateTask(vaultPath, slug, updates) {
348
564
  } else {
349
565
  newFrontmatter.blocked_by = updates.blocked_by;
350
566
  }
351
- } else if (updates.status && updates.status !== "blocked") {
567
+ } else if (updates.status !== void 0 && updates.status !== "blocked") {
352
568
  delete newFrontmatter.blocked_by;
353
569
  }
354
- const fileContent = matter.stringify(task.content, newFrontmatter);
355
- fs.writeFileSync(task.path, fileContent);
356
- return {
357
- ...task,
358
- frontmatter: newFrontmatter
359
- };
360
- }
361
- function completeTask(vaultPath, slug) {
362
- const task = readTask(vaultPath, slug);
363
- if (!task) {
364
- throw new Error(`Task not found: ${slug}`);
570
+ persistTaskFrontmatter(task, newFrontmatter);
571
+ const transition = options.skipTransition ? null : resolveStatusTransition(previousStatus, newFrontmatter.status);
572
+ if (transition) {
573
+ const confidence = options.confidence ?? (typeof updates.confidence === "number" ? updates.confidence : void 0);
574
+ const reason = options.reason ?? updates.reason ?? null;
575
+ newFrontmatter = logStatusTransition({
576
+ vaultPath,
577
+ task,
578
+ fromStatus: transition.fromStatus,
579
+ toStatus: transition.toStatus,
580
+ frontmatter: newFrontmatter,
581
+ options: {
582
+ confidence,
583
+ reason
584
+ }
585
+ });
365
586
  }
366
- const now = (/* @__PURE__ */ new Date()).toISOString();
367
- const newFrontmatter = {
368
- ...task.frontmatter,
369
- status: "done",
370
- updated: now,
371
- completed: now
372
- };
373
- delete newFrontmatter.blocked_by;
374
- const fileContent = matter.stringify(task.content, newFrontmatter);
375
- fs.writeFileSync(task.path, fileContent);
376
587
  return {
377
588
  ...task,
378
589
  frontmatter: newFrontmatter
379
590
  };
380
591
  }
592
+ function completeTask(vaultPath, slug, options = {}) {
593
+ return updateTask(vaultPath, slug, { status: "done" }, options);
594
+ }
381
595
  function createBacklogItem(vaultPath, title, options = {}) {
382
596
  ensureBacklogDir(vaultPath);
383
597
  const slug = slugify(title);
384
598
  const backlogPath = getBacklogPath(vaultPath, slug);
385
- if (fs.existsSync(backlogPath)) {
599
+ if (fs2.existsSync(backlogPath)) {
386
600
  throw new Error(`Backlog item already exists: ${slug}`);
387
601
  }
388
602
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -408,7 +622,7 @@ ${options.content}
408
622
  `;
409
623
  }
410
624
  const fileContent = matter.stringify(content, frontmatter);
411
- fs.writeFileSync(backlogPath, fileContent);
625
+ fs2.writeFileSync(backlogPath, fileContent);
412
626
  return {
413
627
  slug,
414
628
  title,
@@ -430,7 +644,7 @@ function updateBacklogItem(vaultPath, slug, updates) {
430
644
  if (updates.tags !== void 0) newFrontmatter.tags = updates.tags;
431
645
  if (updates.lastSeen !== void 0) newFrontmatter.lastSeen = updates.lastSeen;
432
646
  const fileContent = matter.stringify(backlogItem.content, newFrontmatter);
433
- fs.writeFileSync(backlogItem.path, fileContent);
647
+ fs2.writeFileSync(backlogItem.path, fileContent);
434
648
  return {
435
649
  ...backlogItem,
436
650
  frontmatter: newFrontmatter
@@ -450,7 +664,7 @@ function promoteBacklogItem(vaultPath, slug, options = {}) {
450
664
  // Remove title from content
451
665
  tags: backlogItem.frontmatter.tags
452
666
  });
453
- fs.unlinkSync(backlogItem.path);
667
+ fs2.unlinkSync(backlogItem.path);
454
668
  return task;
455
669
  }
456
670
  function getBlockedTasks(vaultPath, project) {
@@ -509,6 +723,13 @@ function getStatusDisplay(status) {
509
723
  }
510
724
 
511
725
  export {
726
+ isRegression,
727
+ appendTransition,
728
+ buildTransitionEvent,
729
+ readAllTransitions,
730
+ queryTransitions,
731
+ countBlockedTransitions,
732
+ formatTransitionsTable,
512
733
  slugify,
513
734
  getTasksDir,
514
735
  getBacklogDir,