clawvault 2.2.1 → 2.3.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/bin/clawvault.js +6 -0
- package/bin/register-task-commands.js +257 -0
- package/dist/chunk-MDIH26GC.js +183 -0
- package/dist/chunk-NGVAEFT2.js +352 -0
- package/dist/commands/archive.js +1 -1
- package/dist/commands/backlog.d.ts +53 -0
- package/dist/commands/backlog.js +119 -0
- package/dist/commands/blocked.d.ts +25 -0
- package/dist/commands/blocked.js +43 -0
- package/dist/commands/canvas.d.ts +20 -0
- package/dist/commands/canvas.js +309 -0
- package/dist/commands/context.js +3 -3
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/graph.js +2 -2
- package/dist/commands/link.js +3 -3
- package/dist/commands/migrate-observations.js +2 -2
- package/dist/commands/observe.js +3 -3
- package/dist/commands/rebuild.js +2 -2
- package/dist/commands/recover.js +2 -2
- package/dist/commands/reflect.js +1 -1
- package/dist/commands/replay.js +3 -3
- package/dist/commands/sleep.js +5 -5
- package/dist/commands/status.js +1 -1
- package/dist/commands/task.d.ts +71 -0
- package/dist/commands/task.js +189 -0
- package/dist/commands/wake.js +7 -7
- package/dist/index.js +38 -38
- package/dist/lib/canvas-layout.d.ts +115 -0
- package/dist/lib/canvas-layout.js +34 -0
- package/dist/lib/task-utils.d.ts +159 -0
- package/dist/lib/task-utils.js +46 -0
- package/package.json +2 -2
- package/dist/{chunk-WZI3OAE5.js → chunk-5WR6RRPX.js} +3 -3
- package/dist/{chunk-L6NB43WV.js → chunk-6BBTI7NV.js} +3 -3
- package/dist/{chunk-73P7XCQM.js → chunk-DPS7NYIU.js} +3 -3
- package/dist/{chunk-MILVYUPK.js → chunk-IWYZAXKJ.js} +3 -3
- package/dist/{chunk-H7JW4L7H.js → chunk-OZ7RIXTO.js} +3 -3
- package/dist/{chunk-LB6P4CD5.js → chunk-PTSEIWXZ.js} +6 -6
- package/dist/{chunk-I5X6J4FX.js → chunk-SOTWYGH7.js} +6 -6
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
// src/lib/task-utils.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import matter from "gray-matter";
|
|
5
|
+
function slugify(text) {
|
|
6
|
+
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").trim();
|
|
7
|
+
}
|
|
8
|
+
function getTasksDir(vaultPath) {
|
|
9
|
+
return path.join(path.resolve(vaultPath), "tasks");
|
|
10
|
+
}
|
|
11
|
+
function getBacklogDir(vaultPath) {
|
|
12
|
+
return path.join(path.resolve(vaultPath), "backlog");
|
|
13
|
+
}
|
|
14
|
+
function ensureTasksDir(vaultPath) {
|
|
15
|
+
const tasksDir = getTasksDir(vaultPath);
|
|
16
|
+
if (!fs.existsSync(tasksDir)) {
|
|
17
|
+
fs.mkdirSync(tasksDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function ensureBacklogDir(vaultPath) {
|
|
21
|
+
const backlogDir = getBacklogDir(vaultPath);
|
|
22
|
+
if (!fs.existsSync(backlogDir)) {
|
|
23
|
+
fs.mkdirSync(backlogDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function getTaskPath(vaultPath, slug) {
|
|
27
|
+
return path.join(getTasksDir(vaultPath), `${slug}.md`);
|
|
28
|
+
}
|
|
29
|
+
function getBacklogPath(vaultPath, slug) {
|
|
30
|
+
return path.join(getBacklogDir(vaultPath), `${slug}.md`);
|
|
31
|
+
}
|
|
32
|
+
function extractTitle(content) {
|
|
33
|
+
const match = content.match(/^#\s+(.+)$/m);
|
|
34
|
+
return match ? match[1].trim() : "";
|
|
35
|
+
}
|
|
36
|
+
function readTask(vaultPath, slug) {
|
|
37
|
+
const taskPath = getTaskPath(vaultPath, slug);
|
|
38
|
+
if (!fs.existsSync(taskPath)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const raw = fs.readFileSync(taskPath, "utf-8");
|
|
43
|
+
const { data, content } = matter(raw);
|
|
44
|
+
const title = extractTitle(content) || slug;
|
|
45
|
+
return {
|
|
46
|
+
slug,
|
|
47
|
+
title,
|
|
48
|
+
content,
|
|
49
|
+
frontmatter: data,
|
|
50
|
+
path: taskPath
|
|
51
|
+
};
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function readBacklogItem(vaultPath, slug) {
|
|
57
|
+
const backlogPath = getBacklogPath(vaultPath, slug);
|
|
58
|
+
if (!fs.existsSync(backlogPath)) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const raw = fs.readFileSync(backlogPath, "utf-8");
|
|
63
|
+
const { data, content } = matter(raw);
|
|
64
|
+
const title = extractTitle(content) || slug;
|
|
65
|
+
return {
|
|
66
|
+
slug,
|
|
67
|
+
title,
|
|
68
|
+
content,
|
|
69
|
+
frontmatter: data,
|
|
70
|
+
path: backlogPath
|
|
71
|
+
};
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function listTasks(vaultPath, filters) {
|
|
77
|
+
const tasksDir = getTasksDir(vaultPath);
|
|
78
|
+
if (!fs.existsSync(tasksDir)) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const tasks = [];
|
|
82
|
+
const entries = fs.readdirSync(tasksDir, { withFileTypes: true });
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const slug = entry.name.replace(/\.md$/, "");
|
|
88
|
+
const task = readTask(vaultPath, slug);
|
|
89
|
+
if (!task) continue;
|
|
90
|
+
if (filters) {
|
|
91
|
+
if (filters.status && task.frontmatter.status !== filters.status) continue;
|
|
92
|
+
if (filters.owner && task.frontmatter.owner !== filters.owner) continue;
|
|
93
|
+
if (filters.project && task.frontmatter.project !== filters.project) continue;
|
|
94
|
+
if (filters.priority && task.frontmatter.priority !== filters.priority) continue;
|
|
95
|
+
}
|
|
96
|
+
tasks.push(task);
|
|
97
|
+
}
|
|
98
|
+
const priorityOrder = {
|
|
99
|
+
critical: 0,
|
|
100
|
+
high: 1,
|
|
101
|
+
medium: 2,
|
|
102
|
+
low: 3
|
|
103
|
+
};
|
|
104
|
+
return tasks.sort((a, b) => {
|
|
105
|
+
const aPriority = priorityOrder[a.frontmatter.priority || "low"];
|
|
106
|
+
const bPriority = priorityOrder[b.frontmatter.priority || "low"];
|
|
107
|
+
if (aPriority !== bPriority) {
|
|
108
|
+
return aPriority - bPriority;
|
|
109
|
+
}
|
|
110
|
+
return new Date(b.frontmatter.created).getTime() - new Date(a.frontmatter.created).getTime();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function listBacklogItems(vaultPath, filters) {
|
|
114
|
+
const backlogDir = getBacklogDir(vaultPath);
|
|
115
|
+
if (!fs.existsSync(backlogDir)) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const items = [];
|
|
119
|
+
const entries = fs.readdirSync(backlogDir, { withFileTypes: true });
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const slug = entry.name.replace(/\.md$/, "");
|
|
125
|
+
const item = readBacklogItem(vaultPath, slug);
|
|
126
|
+
if (!item) continue;
|
|
127
|
+
if (filters) {
|
|
128
|
+
if (filters.project && item.frontmatter.project !== filters.project) continue;
|
|
129
|
+
if (filters.source && item.frontmatter.source !== filters.source) continue;
|
|
130
|
+
}
|
|
131
|
+
items.push(item);
|
|
132
|
+
}
|
|
133
|
+
return items.sort((a, b) => {
|
|
134
|
+
return new Date(b.frontmatter.created).getTime() - new Date(a.frontmatter.created).getTime();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function createTask(vaultPath, title, options = {}) {
|
|
138
|
+
ensureTasksDir(vaultPath);
|
|
139
|
+
const slug = slugify(title);
|
|
140
|
+
const taskPath = getTaskPath(vaultPath, slug);
|
|
141
|
+
if (fs.existsSync(taskPath)) {
|
|
142
|
+
throw new Error(`Task already exists: ${slug}`);
|
|
143
|
+
}
|
|
144
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
145
|
+
const frontmatter = {
|
|
146
|
+
status: "open",
|
|
147
|
+
created: now,
|
|
148
|
+
updated: now
|
|
149
|
+
};
|
|
150
|
+
if (options.owner) frontmatter.owner = options.owner;
|
|
151
|
+
if (options.project) frontmatter.project = options.project;
|
|
152
|
+
if (options.priority) frontmatter.priority = options.priority;
|
|
153
|
+
if (options.due) frontmatter.due = options.due;
|
|
154
|
+
if (options.tags && options.tags.length > 0) frontmatter.tags = options.tags;
|
|
155
|
+
let content = `# ${title}
|
|
156
|
+
`;
|
|
157
|
+
const links = [];
|
|
158
|
+
if (options.owner) links.push(`[[${options.owner}]]`);
|
|
159
|
+
if (options.project) links.push(`[[${options.project}]]`);
|
|
160
|
+
if (links.length > 0) {
|
|
161
|
+
content += `
|
|
162
|
+
${links.join(" | ")}
|
|
163
|
+
`;
|
|
164
|
+
}
|
|
165
|
+
if (options.content) {
|
|
166
|
+
content += `
|
|
167
|
+
${options.content}
|
|
168
|
+
`;
|
|
169
|
+
}
|
|
170
|
+
const fileContent = matter.stringify(content, frontmatter);
|
|
171
|
+
fs.writeFileSync(taskPath, fileContent);
|
|
172
|
+
return {
|
|
173
|
+
slug,
|
|
174
|
+
title,
|
|
175
|
+
content,
|
|
176
|
+
frontmatter,
|
|
177
|
+
path: taskPath
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function updateTask(vaultPath, slug, updates) {
|
|
181
|
+
const task = readTask(vaultPath, slug);
|
|
182
|
+
if (!task) {
|
|
183
|
+
throw new Error(`Task not found: ${slug}`);
|
|
184
|
+
}
|
|
185
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
186
|
+
const newFrontmatter = {
|
|
187
|
+
...task.frontmatter,
|
|
188
|
+
updated: now
|
|
189
|
+
};
|
|
190
|
+
if (updates.status !== void 0) newFrontmatter.status = updates.status;
|
|
191
|
+
if (updates.owner !== void 0) newFrontmatter.owner = updates.owner;
|
|
192
|
+
if (updates.project !== void 0) newFrontmatter.project = updates.project;
|
|
193
|
+
if (updates.priority !== void 0) newFrontmatter.priority = updates.priority;
|
|
194
|
+
if (updates.due !== void 0) newFrontmatter.due = updates.due;
|
|
195
|
+
if (updates.tags !== void 0) newFrontmatter.tags = updates.tags;
|
|
196
|
+
if (updates.blocked_by !== void 0) {
|
|
197
|
+
newFrontmatter.blocked_by = updates.blocked_by;
|
|
198
|
+
} else if (updates.status && updates.status !== "blocked") {
|
|
199
|
+
delete newFrontmatter.blocked_by;
|
|
200
|
+
}
|
|
201
|
+
const fileContent = matter.stringify(task.content, newFrontmatter);
|
|
202
|
+
fs.writeFileSync(task.path, fileContent);
|
|
203
|
+
return {
|
|
204
|
+
...task,
|
|
205
|
+
frontmatter: newFrontmatter
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function completeTask(vaultPath, slug) {
|
|
209
|
+
const task = readTask(vaultPath, slug);
|
|
210
|
+
if (!task) {
|
|
211
|
+
throw new Error(`Task not found: ${slug}`);
|
|
212
|
+
}
|
|
213
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
214
|
+
const newFrontmatter = {
|
|
215
|
+
...task.frontmatter,
|
|
216
|
+
status: "done",
|
|
217
|
+
updated: now,
|
|
218
|
+
completed: now
|
|
219
|
+
};
|
|
220
|
+
delete newFrontmatter.blocked_by;
|
|
221
|
+
const fileContent = matter.stringify(task.content, newFrontmatter);
|
|
222
|
+
fs.writeFileSync(task.path, fileContent);
|
|
223
|
+
return {
|
|
224
|
+
...task,
|
|
225
|
+
frontmatter: newFrontmatter
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function createBacklogItem(vaultPath, title, options = {}) {
|
|
229
|
+
ensureBacklogDir(vaultPath);
|
|
230
|
+
const slug = slugify(title);
|
|
231
|
+
const backlogPath = getBacklogPath(vaultPath, slug);
|
|
232
|
+
if (fs.existsSync(backlogPath)) {
|
|
233
|
+
throw new Error(`Backlog item already exists: ${slug}`);
|
|
234
|
+
}
|
|
235
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
236
|
+
const frontmatter = {
|
|
237
|
+
created: now
|
|
238
|
+
};
|
|
239
|
+
if (options.source) frontmatter.source = options.source;
|
|
240
|
+
if (options.project) frontmatter.project = options.project;
|
|
241
|
+
if (options.tags && options.tags.length > 0) frontmatter.tags = options.tags;
|
|
242
|
+
let content = `# ${title}
|
|
243
|
+
`;
|
|
244
|
+
const links = [];
|
|
245
|
+
if (options.source) links.push(`[[${options.source}]]`);
|
|
246
|
+
if (options.project) links.push(`[[${options.project}]]`);
|
|
247
|
+
if (links.length > 0) {
|
|
248
|
+
content += `
|
|
249
|
+
${links.join(" | ")}
|
|
250
|
+
`;
|
|
251
|
+
}
|
|
252
|
+
if (options.content) {
|
|
253
|
+
content += `
|
|
254
|
+
${options.content}
|
|
255
|
+
`;
|
|
256
|
+
}
|
|
257
|
+
const fileContent = matter.stringify(content, frontmatter);
|
|
258
|
+
fs.writeFileSync(backlogPath, fileContent);
|
|
259
|
+
return {
|
|
260
|
+
slug,
|
|
261
|
+
title,
|
|
262
|
+
content,
|
|
263
|
+
frontmatter,
|
|
264
|
+
path: backlogPath
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function promoteBacklogItem(vaultPath, slug, options = {}) {
|
|
268
|
+
const backlogItem = readBacklogItem(vaultPath, slug);
|
|
269
|
+
if (!backlogItem) {
|
|
270
|
+
throw new Error(`Backlog item not found: ${slug}`);
|
|
271
|
+
}
|
|
272
|
+
const task = createTask(vaultPath, backlogItem.title, {
|
|
273
|
+
owner: options.owner,
|
|
274
|
+
project: backlogItem.frontmatter.project,
|
|
275
|
+
priority: options.priority,
|
|
276
|
+
due: options.due,
|
|
277
|
+
content: backlogItem.content.replace(/^#\s+.+\n/, "").trim(),
|
|
278
|
+
// Remove title from content
|
|
279
|
+
tags: backlogItem.frontmatter.tags
|
|
280
|
+
});
|
|
281
|
+
fs.unlinkSync(backlogItem.path);
|
|
282
|
+
return task;
|
|
283
|
+
}
|
|
284
|
+
function getBlockedTasks(vaultPath, project) {
|
|
285
|
+
const filters = { status: "blocked" };
|
|
286
|
+
if (project) filters.project = project;
|
|
287
|
+
return listTasks(vaultPath, filters);
|
|
288
|
+
}
|
|
289
|
+
function getActiveTasks(vaultPath, filters) {
|
|
290
|
+
const allTasks = listTasks(vaultPath, filters);
|
|
291
|
+
return allTasks.filter((t) => t.frontmatter.status === "open" || t.frontmatter.status === "in-progress");
|
|
292
|
+
}
|
|
293
|
+
function getRecentlyCompletedTasks(vaultPath, limit = 10) {
|
|
294
|
+
const allTasks = listTasks(vaultPath, { status: "done" });
|
|
295
|
+
return allTasks.filter((t) => t.frontmatter.completed).sort((a, b) => {
|
|
296
|
+
const aCompleted = new Date(a.frontmatter.completed || 0).getTime();
|
|
297
|
+
const bCompleted = new Date(b.frontmatter.completed || 0).getTime();
|
|
298
|
+
return bCompleted - aCompleted;
|
|
299
|
+
}).slice(0, limit);
|
|
300
|
+
}
|
|
301
|
+
function getStatusIcon(status) {
|
|
302
|
+
switch (status) {
|
|
303
|
+
case "in-progress":
|
|
304
|
+
return "\u25CF";
|
|
305
|
+
case "blocked":
|
|
306
|
+
return "\u25A0";
|
|
307
|
+
case "open":
|
|
308
|
+
return "\u25CB";
|
|
309
|
+
case "done":
|
|
310
|
+
return "\u2713";
|
|
311
|
+
default:
|
|
312
|
+
return "\u25CB";
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function getStatusDisplay(status) {
|
|
316
|
+
switch (status) {
|
|
317
|
+
case "in-progress":
|
|
318
|
+
return "active";
|
|
319
|
+
case "blocked":
|
|
320
|
+
return "blocked";
|
|
321
|
+
case "open":
|
|
322
|
+
return "open";
|
|
323
|
+
case "done":
|
|
324
|
+
return "done";
|
|
325
|
+
default:
|
|
326
|
+
return status;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export {
|
|
331
|
+
slugify,
|
|
332
|
+
getTasksDir,
|
|
333
|
+
getBacklogDir,
|
|
334
|
+
ensureTasksDir,
|
|
335
|
+
ensureBacklogDir,
|
|
336
|
+
getTaskPath,
|
|
337
|
+
getBacklogPath,
|
|
338
|
+
readTask,
|
|
339
|
+
readBacklogItem,
|
|
340
|
+
listTasks,
|
|
341
|
+
listBacklogItems,
|
|
342
|
+
createTask,
|
|
343
|
+
updateTask,
|
|
344
|
+
completeTask,
|
|
345
|
+
createBacklogItem,
|
|
346
|
+
promoteBacklogItem,
|
|
347
|
+
getBlockedTasks,
|
|
348
|
+
getActiveTasks,
|
|
349
|
+
getRecentlyCompletedTasks,
|
|
350
|
+
getStatusIcon,
|
|
351
|
+
getStatusDisplay
|
|
352
|
+
};
|
package/dist/commands/archive.js
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { TaskPriority, BacklogItem, Task } from '../lib/task-utils.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Backlog command for ClawVault
|
|
5
|
+
* Manages backlog add/list/promote operations
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface BacklogAddOptions {
|
|
9
|
+
source?: string;
|
|
10
|
+
project?: string;
|
|
11
|
+
content?: string;
|
|
12
|
+
tags?: string[];
|
|
13
|
+
}
|
|
14
|
+
interface BacklogListOptions {
|
|
15
|
+
project?: string;
|
|
16
|
+
json?: boolean;
|
|
17
|
+
}
|
|
18
|
+
interface BacklogPromoteOptions {
|
|
19
|
+
owner?: string;
|
|
20
|
+
priority?: TaskPriority;
|
|
21
|
+
due?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Add a new backlog item
|
|
25
|
+
*/
|
|
26
|
+
declare function backlogAdd(vaultPath: string, title: string, options?: BacklogAddOptions): BacklogItem;
|
|
27
|
+
/**
|
|
28
|
+
* List backlog items with optional filters
|
|
29
|
+
*/
|
|
30
|
+
declare function backlogList(vaultPath: string, options?: BacklogListOptions): BacklogItem[];
|
|
31
|
+
/**
|
|
32
|
+
* Promote a backlog item to a task
|
|
33
|
+
*/
|
|
34
|
+
declare function backlogPromote(vaultPath: string, slug: string, options?: BacklogPromoteOptions): Task;
|
|
35
|
+
/**
|
|
36
|
+
* Format backlog list for terminal display
|
|
37
|
+
*/
|
|
38
|
+
declare function formatBacklogList(items: BacklogItem[]): string;
|
|
39
|
+
/**
|
|
40
|
+
* Format backlog item details for display
|
|
41
|
+
*/
|
|
42
|
+
declare function formatBacklogDetails(item: BacklogItem): string;
|
|
43
|
+
/**
|
|
44
|
+
* Backlog command handler for CLI
|
|
45
|
+
* Note: The CLI uses "clawvault backlog <title>" as shorthand for add
|
|
46
|
+
*/
|
|
47
|
+
declare function backlogCommand(vaultPath: string, action: 'add' | 'list' | 'promote', args: {
|
|
48
|
+
title?: string;
|
|
49
|
+
slug?: string;
|
|
50
|
+
options?: BacklogAddOptions & BacklogListOptions & BacklogPromoteOptions;
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
|
|
53
|
+
export { type BacklogAddOptions, type BacklogListOptions, type BacklogPromoteOptions, backlogAdd, backlogCommand, backlogList, backlogPromote, formatBacklogDetails, formatBacklogList };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createBacklogItem,
|
|
3
|
+
listBacklogItems,
|
|
4
|
+
promoteBacklogItem
|
|
5
|
+
} from "../chunk-NGVAEFT2.js";
|
|
6
|
+
|
|
7
|
+
// src/commands/backlog.ts
|
|
8
|
+
function backlogAdd(vaultPath, title, options = {}) {
|
|
9
|
+
return createBacklogItem(vaultPath, title, {
|
|
10
|
+
source: options.source,
|
|
11
|
+
project: options.project,
|
|
12
|
+
content: options.content,
|
|
13
|
+
tags: options.tags
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function backlogList(vaultPath, options = {}) {
|
|
17
|
+
const filters = {};
|
|
18
|
+
if (options.project) filters.project = options.project;
|
|
19
|
+
return listBacklogItems(vaultPath, filters);
|
|
20
|
+
}
|
|
21
|
+
function backlogPromote(vaultPath, slug, options = {}) {
|
|
22
|
+
return promoteBacklogItem(vaultPath, slug, {
|
|
23
|
+
owner: options.owner,
|
|
24
|
+
priority: options.priority,
|
|
25
|
+
due: options.due
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function formatBacklogList(items) {
|
|
29
|
+
if (items.length === 0) {
|
|
30
|
+
return "No backlog items found.\n";
|
|
31
|
+
}
|
|
32
|
+
const headers = ["SOURCE", "PROJECT", "CREATED", "TITLE"];
|
|
33
|
+
const widths = [12, 16, 12, 40];
|
|
34
|
+
let output = headers.map((h, i) => h.padEnd(widths[i])).join(" ") + "\n";
|
|
35
|
+
for (const item of items) {
|
|
36
|
+
const source = item.frontmatter.source || "-";
|
|
37
|
+
const project = item.frontmatter.project || "-";
|
|
38
|
+
const created = item.frontmatter.created.split("T")[0];
|
|
39
|
+
const title = item.title.length > widths[3] ? item.title.slice(0, widths[3] - 3) + "..." : item.title;
|
|
40
|
+
const row = [
|
|
41
|
+
source.padEnd(widths[0]),
|
|
42
|
+
project.padEnd(widths[1]),
|
|
43
|
+
created.padEnd(widths[2]),
|
|
44
|
+
title
|
|
45
|
+
];
|
|
46
|
+
output += row.join(" ") + "\n";
|
|
47
|
+
}
|
|
48
|
+
return output;
|
|
49
|
+
}
|
|
50
|
+
function formatBacklogDetails(item) {
|
|
51
|
+
let output = "";
|
|
52
|
+
output += `# ${item.title}
|
|
53
|
+
`;
|
|
54
|
+
output += "-".repeat(40) + "\n";
|
|
55
|
+
if (item.frontmatter.source) {
|
|
56
|
+
output += `Source: ${item.frontmatter.source}
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
if (item.frontmatter.project) {
|
|
60
|
+
output += `Project: ${item.frontmatter.project}
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
if (item.frontmatter.tags && item.frontmatter.tags.length > 0) {
|
|
64
|
+
output += `Tags: ${item.frontmatter.tags.join(", ")}
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
output += `Created: ${item.frontmatter.created}
|
|
68
|
+
`;
|
|
69
|
+
output += `File: ${item.path}
|
|
70
|
+
`;
|
|
71
|
+
output += "-".repeat(40) + "\n";
|
|
72
|
+
const contentWithoutTitle = item.content.replace(/^#\s+.+\n/, "").trim();
|
|
73
|
+
if (contentWithoutTitle) {
|
|
74
|
+
output += "\n" + contentWithoutTitle + "\n";
|
|
75
|
+
}
|
|
76
|
+
return output;
|
|
77
|
+
}
|
|
78
|
+
async function backlogCommand(vaultPath, action, args) {
|
|
79
|
+
const options = args.options || {};
|
|
80
|
+
switch (action) {
|
|
81
|
+
case "add": {
|
|
82
|
+
if (!args.title) {
|
|
83
|
+
throw new Error("Title is required for backlog add");
|
|
84
|
+
}
|
|
85
|
+
const item = backlogAdd(vaultPath, args.title, options);
|
|
86
|
+
console.log(`\u2713 Added to backlog: ${item.slug}`);
|
|
87
|
+
console.log(` Path: ${item.path}`);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
case "list": {
|
|
91
|
+
const items = backlogList(vaultPath, options);
|
|
92
|
+
if (options.json) {
|
|
93
|
+
console.log(JSON.stringify(items, null, 2));
|
|
94
|
+
} else {
|
|
95
|
+
console.log(formatBacklogList(items));
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "promote": {
|
|
100
|
+
if (!args.slug) {
|
|
101
|
+
throw new Error("Backlog item slug is required for promote");
|
|
102
|
+
}
|
|
103
|
+
const task = backlogPromote(vaultPath, args.slug, options);
|
|
104
|
+
console.log(`\u2713 Promoted to task: ${task.slug}`);
|
|
105
|
+
console.log(` Path: ${task.path}`);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
default:
|
|
109
|
+
throw new Error(`Unknown backlog action: ${action}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
backlogAdd,
|
|
114
|
+
backlogCommand,
|
|
115
|
+
backlogList,
|
|
116
|
+
backlogPromote,
|
|
117
|
+
formatBacklogDetails,
|
|
118
|
+
formatBacklogList
|
|
119
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Task } from '../lib/task-utils.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Blocked command for ClawVault
|
|
5
|
+
* Quick view of blocked tasks
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface BlockedOptions {
|
|
9
|
+
project?: string;
|
|
10
|
+
json?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get blocked tasks
|
|
14
|
+
*/
|
|
15
|
+
declare function blockedList(vaultPath: string, options?: BlockedOptions): Task[];
|
|
16
|
+
/**
|
|
17
|
+
* Format blocked tasks for terminal display
|
|
18
|
+
*/
|
|
19
|
+
declare function formatBlockedList(tasks: Task[]): string;
|
|
20
|
+
/**
|
|
21
|
+
* Blocked command handler for CLI
|
|
22
|
+
*/
|
|
23
|
+
declare function blockedCommand(vaultPath: string, options?: BlockedOptions): Promise<void>;
|
|
24
|
+
|
|
25
|
+
export { type BlockedOptions, blockedCommand, blockedList, formatBlockedList };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBlockedTasks
|
|
3
|
+
} from "../chunk-NGVAEFT2.js";
|
|
4
|
+
|
|
5
|
+
// src/commands/blocked.ts
|
|
6
|
+
function blockedList(vaultPath, options = {}) {
|
|
7
|
+
return getBlockedTasks(vaultPath, options.project);
|
|
8
|
+
}
|
|
9
|
+
function formatBlockedList(tasks) {
|
|
10
|
+
if (tasks.length === 0) {
|
|
11
|
+
return "No blocked tasks.\n";
|
|
12
|
+
}
|
|
13
|
+
let output = `BLOCKED TASKS (${tasks.length})
|
|
14
|
+
|
|
15
|
+
`;
|
|
16
|
+
for (const task of tasks) {
|
|
17
|
+
const owner = task.frontmatter.owner || "unassigned";
|
|
18
|
+
const project = task.frontmatter.project || "no project";
|
|
19
|
+
const blockedBy = task.frontmatter.blocked_by || "unknown";
|
|
20
|
+
const updatedDate = task.frontmatter.updated.split("T")[0];
|
|
21
|
+
output += `\u25A0 ${task.title} (${owner}, ${project})
|
|
22
|
+
`;
|
|
23
|
+
output += ` Blocked by: ${blockedBy}
|
|
24
|
+
`;
|
|
25
|
+
output += ` Since: ${updatedDate}
|
|
26
|
+
`;
|
|
27
|
+
output += "\n";
|
|
28
|
+
}
|
|
29
|
+
return output;
|
|
30
|
+
}
|
|
31
|
+
async function blockedCommand(vaultPath, options = {}) {
|
|
32
|
+
const tasks = blockedList(vaultPath, options);
|
|
33
|
+
if (options.json) {
|
|
34
|
+
console.log(JSON.stringify(tasks, null, 2));
|
|
35
|
+
} else {
|
|
36
|
+
console.log(formatBlockedList(tasks));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
blockedCommand,
|
|
41
|
+
blockedList,
|
|
42
|
+
formatBlockedList
|
|
43
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Canvas } from '../lib/canvas-layout.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Canvas command for ClawVault
|
|
5
|
+
* Generates an Obsidian JSON Canvas dashboard
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface CanvasOptions {
|
|
9
|
+
output?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate the canvas dashboard
|
|
13
|
+
*/
|
|
14
|
+
declare function generateCanvas(vaultPath: string): Canvas;
|
|
15
|
+
/**
|
|
16
|
+
* Canvas command handler for CLI
|
|
17
|
+
*/
|
|
18
|
+
declare function canvasCommand(vaultPath: string, options?: CanvasOptions): Promise<void>;
|
|
19
|
+
|
|
20
|
+
export { type CanvasOptions, canvasCommand, generateCanvas };
|