md-task-viewer 0.1.9 → 0.1.10
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/README.md +5 -3
- package/dist/cli.js +171 -124
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/index-CSNRoovR.css +1 -0
- package/dist/client/assets/index-j5ntXWRS.js +78 -0
- package/dist/client/index.html +2 -2
- package/dist/server.js +169 -122
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/index-CczpSlHS.js +0 -77
- package/dist/client/assets/index-u2_Kc4rH.css +0 -1
package/dist/client/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Markdown Task Viewer</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-j5ntXWRS.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CSNRoovR.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/dist/server.js
CHANGED
|
@@ -2,32 +2,25 @@
|
|
|
2
2
|
import Fastify from "fastify";
|
|
3
3
|
import fastifyStatic from "@fastify/static";
|
|
4
4
|
import chokidar from "chokidar";
|
|
5
|
-
import
|
|
5
|
+
import path7 from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
|
|
8
|
-
// src/taskStore.ts
|
|
9
|
-
import matter from "gray-matter";
|
|
10
|
-
import picomatch from "picomatch";
|
|
11
|
-
import path from "path";
|
|
12
|
-
import { promises as fs } from "fs";
|
|
13
|
-
|
|
14
|
-
// src/types.ts
|
|
15
|
-
var CONFIG_FILE_NAME = ".md-task-viewer.json";
|
|
16
|
-
|
|
17
8
|
// src/slugify.ts
|
|
18
9
|
function slugify(value) {
|
|
19
10
|
const slug = value.normalize("NFC").replace(/[\s\u3000]+/g, "-").replace(/[^\p{L}\p{N}-]+/gu, "").replace(/^-+|-+$/g, "");
|
|
20
11
|
return slug || "untitled-task";
|
|
21
12
|
}
|
|
22
13
|
|
|
23
|
-
// src/taskStore.ts
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
// src/taskStore/paths.ts
|
|
15
|
+
import path from "path";
|
|
16
|
+
|
|
17
|
+
// src/taskStore/errors.ts
|
|
27
18
|
var ConflictError = class extends Error {
|
|
28
19
|
};
|
|
29
20
|
var ValidationError = class extends Error {
|
|
30
21
|
};
|
|
22
|
+
|
|
23
|
+
// src/taskStore/paths.ts
|
|
31
24
|
function toPosixPath(filePath) {
|
|
32
25
|
return filePath.split(path.sep).join("/");
|
|
33
26
|
}
|
|
@@ -41,11 +34,30 @@ function normalizeRelativePath(candidate) {
|
|
|
41
34
|
function ensureMarkdownExtension(filePath) {
|
|
42
35
|
return path.posix.extname(filePath) ? filePath : `${filePath}.md`;
|
|
43
36
|
}
|
|
37
|
+
|
|
38
|
+
// src/taskStore/frontmatter.ts
|
|
39
|
+
import matter from "gray-matter";
|
|
40
|
+
import path2 from "path";
|
|
41
|
+
import { promises as fs } from "fs";
|
|
42
|
+
var REQUIRED_PRIORITY = ["MUST", "WANT"];
|
|
43
|
+
var REQUIRED_STATUS = ["TODO", "DONE"];
|
|
44
|
+
function isValidPriority(value) {
|
|
45
|
+
return REQUIRED_PRIORITY.includes(value);
|
|
46
|
+
}
|
|
47
|
+
function isValidStatus(value) {
|
|
48
|
+
return REQUIRED_STATUS.includes(value);
|
|
49
|
+
}
|
|
50
|
+
function ensureRequiredStatus(status) {
|
|
51
|
+
if (!isValidStatus(status)) {
|
|
52
|
+
throw new ValidationError("Status must be TODO or DONE.");
|
|
53
|
+
}
|
|
54
|
+
return status;
|
|
55
|
+
}
|
|
44
56
|
function asUtcISOString(date) {
|
|
45
57
|
return date.toISOString();
|
|
46
58
|
}
|
|
47
59
|
function buildDefaults(filePath, stats) {
|
|
48
|
-
const basename =
|
|
60
|
+
const basename = path2.basename(filePath, path2.extname(filePath));
|
|
49
61
|
const title = basename.replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
50
62
|
return {
|
|
51
63
|
title,
|
|
@@ -63,8 +75,8 @@ function splitFrontmatter(data, statsDefaults) {
|
|
|
63
75
|
}
|
|
64
76
|
}
|
|
65
77
|
const title = typeof data.title === "string" && data.title.trim() ? data.title : statsDefaults.title;
|
|
66
|
-
const priority =
|
|
67
|
-
const status =
|
|
78
|
+
const priority = isValidPriority(data.priority) ? data.priority : statsDefaults.priority;
|
|
79
|
+
const status = isValidStatus(data.status) ? data.status : statsDefaults.status;
|
|
68
80
|
const createdAt = typeof data.createdAt === "string" && !Number.isNaN(Date.parse(data.createdAt)) ? new Date(data.createdAt).toISOString() : statsDefaults.createdAt;
|
|
69
81
|
const updatedAt = typeof data.updatedAt === "string" && !Number.isNaN(Date.parse(data.updatedAt)) ? new Date(data.updatedAt).toISOString() : statsDefaults.updatedAt;
|
|
70
82
|
const normalized = title !== data.title || priority !== data.priority || status !== data.status || createdAt !== data.createdAt || updatedAt !== data.updatedAt;
|
|
@@ -85,53 +97,8 @@ function serializeTask(record) {
|
|
|
85
97
|
};
|
|
86
98
|
return matter.stringify(record.content, data);
|
|
87
99
|
}
|
|
88
|
-
async function readDirectoryRecursive(rootDir, currentDir, results) {
|
|
89
|
-
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
90
|
-
for (const entry of entries) {
|
|
91
|
-
if (entry.name === ".git" || entry.name === "node_modules") {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
const absolutePath = path.join(currentDir, entry.name);
|
|
95
|
-
if (entry.isDirectory()) {
|
|
96
|
-
await readDirectoryRecursive(rootDir, absolutePath, results);
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if (entry.name === CONFIG_FILE_NAME) {
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
if (!MARKDOWN_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
results.push(toPosixPath(path.relative(rootDir, absolutePath)));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
async function listMarkdownFiles(rootDir, taskDirs, ignorePaths) {
|
|
109
|
-
const results = [];
|
|
110
|
-
const seen = /* @__PURE__ */ new Set();
|
|
111
|
-
const isIgnored = ignorePaths.length > 0 ? picomatch(ignorePaths) : null;
|
|
112
|
-
for (const taskDir of taskDirs) {
|
|
113
|
-
const scanDir = path.resolve(rootDir, taskDir);
|
|
114
|
-
try {
|
|
115
|
-
await fs.access(scanDir);
|
|
116
|
-
} catch {
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
const dirResults = [];
|
|
120
|
-
await readDirectoryRecursive(rootDir, scanDir, dirResults);
|
|
121
|
-
for (const filePath of dirResults) {
|
|
122
|
-
if (!seen.has(filePath)) {
|
|
123
|
-
seen.add(filePath);
|
|
124
|
-
if (isIgnored && isIgnored(filePath)) {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
results.push(filePath);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return results.sort();
|
|
132
|
-
}
|
|
133
100
|
async function parseTask(rootDir, relativePath) {
|
|
134
|
-
const absolutePath =
|
|
101
|
+
const absolutePath = path2.join(rootDir, relativePath);
|
|
135
102
|
const raw = await fs.readFile(absolutePath, "utf8");
|
|
136
103
|
const stats = await fs.stat(absolutePath);
|
|
137
104
|
const parsed = matter(raw);
|
|
@@ -146,10 +113,19 @@ async function parseTask(rootDir, relativePath) {
|
|
|
146
113
|
normalized
|
|
147
114
|
};
|
|
148
115
|
}
|
|
116
|
+
|
|
117
|
+
// src/taskStore/config.ts
|
|
118
|
+
import path3 from "path";
|
|
119
|
+
import { promises as fs2 } from "fs";
|
|
120
|
+
|
|
121
|
+
// src/types.ts
|
|
122
|
+
var CONFIG_FILE_NAME = ".md-task-viewer.json";
|
|
123
|
+
|
|
124
|
+
// src/taskStore/config.ts
|
|
149
125
|
async function readConfig(rootDir) {
|
|
150
|
-
const configFilePath =
|
|
126
|
+
const configFilePath = path3.join(rootDir, CONFIG_FILE_NAME);
|
|
151
127
|
try {
|
|
152
|
-
const raw = await
|
|
128
|
+
const raw = await fs2.readFile(configFilePath, "utf8");
|
|
153
129
|
const parsed = JSON.parse(raw);
|
|
154
130
|
const taskDirs = Array.isArray(parsed.taskDirs) ? parsed.taskDirs.filter((item) => typeof item === "string") : ["."];
|
|
155
131
|
const ignorePaths = Array.isArray(parsed.ignorePaths) ? parsed.ignorePaths.filter((item) => typeof item === "string") : [];
|
|
@@ -194,8 +170,14 @@ async function saveOrder(rootDir, order) {
|
|
|
194
170
|
)
|
|
195
171
|
);
|
|
196
172
|
const existing = await readConfig(rootDir);
|
|
197
|
-
const payload = {
|
|
198
|
-
|
|
173
|
+
const payload = {
|
|
174
|
+
version: 1,
|
|
175
|
+
taskDirs: existing.taskDirs,
|
|
176
|
+
ignorePaths: existing.ignorePaths,
|
|
177
|
+
order: normalized,
|
|
178
|
+
commands: existing.commands
|
|
179
|
+
};
|
|
180
|
+
await fs2.writeFile(path3.join(rootDir, CONFIG_FILE_NAME), `${JSON.stringify(payload, null, 2)}
|
|
199
181
|
`, "utf8");
|
|
200
182
|
}
|
|
201
183
|
async function saveConfig(rootDir, taskDirs, ignorePaths, commands) {
|
|
@@ -212,11 +194,102 @@ async function saveConfig(rootDir, taskDirs, ignorePaths, commands) {
|
|
|
212
194
|
const existing = await readConfig(rootDir);
|
|
213
195
|
const validatedIgnorePaths = ignorePaths ?? existing.ignorePaths;
|
|
214
196
|
const validatedCommands = commands !== void 0 ? commands : existing.commands;
|
|
215
|
-
const payload = {
|
|
216
|
-
|
|
197
|
+
const payload = {
|
|
198
|
+
version: 1,
|
|
199
|
+
taskDirs: validated,
|
|
200
|
+
ignorePaths: validatedIgnorePaths,
|
|
201
|
+
order: existing.order,
|
|
202
|
+
commands: validatedCommands
|
|
203
|
+
};
|
|
204
|
+
await fs2.writeFile(path3.join(rootDir, CONFIG_FILE_NAME), `${JSON.stringify(payload, null, 2)}
|
|
217
205
|
`, "utf8");
|
|
218
206
|
return payload;
|
|
219
207
|
}
|
|
208
|
+
function parseOrderPayload(input) {
|
|
209
|
+
if (!Array.isArray(input)) {
|
|
210
|
+
throw new ValidationError("Order payload must be an array.");
|
|
211
|
+
}
|
|
212
|
+
return input.map((item) => ensureMarkdownExtension(normalizeRelativePath(String(item))));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/taskStore/tasks.ts
|
|
216
|
+
import path5 from "path";
|
|
217
|
+
import { promises as fs4 } from "fs";
|
|
218
|
+
|
|
219
|
+
// src/taskStore/scanner.ts
|
|
220
|
+
import picomatch from "picomatch";
|
|
221
|
+
import path4 from "path";
|
|
222
|
+
import { promises as fs3 } from "fs";
|
|
223
|
+
var MARKDOWN_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown"]);
|
|
224
|
+
async function readDirectoryRecursive(rootDir, currentDir, results) {
|
|
225
|
+
const entries = await fs3.readdir(currentDir, { withFileTypes: true });
|
|
226
|
+
for (const entry of entries) {
|
|
227
|
+
if (entry.name === ".git" || entry.name === "node_modules") {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const absolutePath = path4.join(currentDir, entry.name);
|
|
231
|
+
if (entry.isDirectory()) {
|
|
232
|
+
await readDirectoryRecursive(rootDir, absolutePath, results);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (entry.name === CONFIG_FILE_NAME) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (!MARKDOWN_EXTENSIONS.has(path4.extname(entry.name).toLowerCase())) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
results.push(toPosixPath(path4.relative(rootDir, absolutePath)));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
async function listMarkdownFiles(rootDir, taskDirs, ignorePaths) {
|
|
245
|
+
const results = [];
|
|
246
|
+
const seen = /* @__PURE__ */ new Set();
|
|
247
|
+
const isIgnored = ignorePaths.length > 0 ? picomatch(ignorePaths) : null;
|
|
248
|
+
for (const taskDir of taskDirs) {
|
|
249
|
+
const scanDir = path4.resolve(rootDir, taskDir);
|
|
250
|
+
try {
|
|
251
|
+
await fs3.access(scanDir);
|
|
252
|
+
} catch {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const dirResults = [];
|
|
256
|
+
await readDirectoryRecursive(rootDir, scanDir, dirResults);
|
|
257
|
+
for (const filePath of dirResults) {
|
|
258
|
+
if (!seen.has(filePath)) {
|
|
259
|
+
seen.add(filePath);
|
|
260
|
+
if (isIgnored && isIgnored(filePath)) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
results.push(filePath);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return results.sort();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// src/taskStore/tasks.ts
|
|
271
|
+
async function ensureDirectoryForFile(rootDir, relativeFilePath) {
|
|
272
|
+
const normalized = ensureMarkdownExtension(normalizeRelativePath(relativeFilePath));
|
|
273
|
+
const absolutePath = path5.join(rootDir, normalized);
|
|
274
|
+
const directory = path5.dirname(absolutePath);
|
|
275
|
+
await fs4.mkdir(directory, { recursive: true });
|
|
276
|
+
return normalized;
|
|
277
|
+
}
|
|
278
|
+
async function nextAvailablePath(rootDir, directory, title) {
|
|
279
|
+
const safeDirectory = directory ? normalizeRelativePath(directory) : "";
|
|
280
|
+
const slug = slugify(title);
|
|
281
|
+
const base = safeDirectory ? `${safeDirectory}/${slug}` : slug;
|
|
282
|
+
let attempt = 0;
|
|
283
|
+
while (true) {
|
|
284
|
+
const candidate = ensureMarkdownExtension(attempt === 0 ? base : `${base}-${attempt + 1}`);
|
|
285
|
+
try {
|
|
286
|
+
await fs4.access(path5.join(rootDir, candidate));
|
|
287
|
+
attempt += 1;
|
|
288
|
+
} catch {
|
|
289
|
+
return candidate;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
220
293
|
async function listTasks(rootDir) {
|
|
221
294
|
const config = await readConfig(rootDir);
|
|
222
295
|
const files = await listMarkdownFiles(rootDir, config.taskDirs, config.ignorePaths);
|
|
@@ -250,38 +323,17 @@ async function listTasks(rootDir) {
|
|
|
250
323
|
});
|
|
251
324
|
return { tasks: taskRecords, errors };
|
|
252
325
|
}
|
|
253
|
-
async function ensureDirectoryForFile(rootDir, relativeFilePath) {
|
|
254
|
-
const normalized = ensureMarkdownExtension(normalizeRelativePath(relativeFilePath));
|
|
255
|
-
const absolutePath = path.join(rootDir, normalized);
|
|
256
|
-
const directory = path.dirname(absolutePath);
|
|
257
|
-
await fs.mkdir(directory, { recursive: true });
|
|
258
|
-
return normalized;
|
|
259
|
-
}
|
|
260
|
-
async function nextAvailablePath(rootDir, directory, title) {
|
|
261
|
-
const safeDirectory = directory ? normalizeRelativePath(directory) : "";
|
|
262
|
-
const slug = slugify(title);
|
|
263
|
-
const base = safeDirectory ? `${safeDirectory}/${slug}` : slug;
|
|
264
|
-
let attempt = 0;
|
|
265
|
-
while (true) {
|
|
266
|
-
const candidate = ensureMarkdownExtension(attempt === 0 ? base : `${base}-${attempt + 1}`);
|
|
267
|
-
try {
|
|
268
|
-
await fs.access(path.join(rootDir, candidate));
|
|
269
|
-
attempt += 1;
|
|
270
|
-
} catch {
|
|
271
|
-
return candidate;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
326
|
async function createTask(rootDir, input) {
|
|
276
327
|
if (!input.title.trim()) {
|
|
277
328
|
throw new ValidationError("Title is required.");
|
|
278
329
|
}
|
|
330
|
+
const status = input.status !== void 0 ? ensureRequiredStatus(input.status) : "TODO";
|
|
279
331
|
const now = asUtcISOString(/* @__PURE__ */ new Date());
|
|
280
332
|
const relativePath = input.path?.trim() ? await ensureDirectoryForFile(rootDir, input.path) : await nextAvailablePath(rootDir, input.directory ?? "", input.title);
|
|
281
|
-
const absolutePath =
|
|
333
|
+
const absolutePath = path5.join(rootDir, relativePath);
|
|
282
334
|
let targetExists = false;
|
|
283
335
|
try {
|
|
284
|
-
await
|
|
336
|
+
await fs4.access(absolutePath);
|
|
285
337
|
targetExists = true;
|
|
286
338
|
} catch (error) {
|
|
287
339
|
const maybeError = error;
|
|
@@ -301,13 +353,13 @@ async function createTask(rootDir, input) {
|
|
|
301
353
|
frontmatter: {
|
|
302
354
|
title: input.title.trim(),
|
|
303
355
|
priority: input.priority ?? "MUST",
|
|
304
|
-
status
|
|
356
|
+
status,
|
|
305
357
|
createdAt: now,
|
|
306
358
|
updatedAt: now
|
|
307
359
|
}
|
|
308
360
|
};
|
|
309
|
-
await
|
|
310
|
-
await
|
|
361
|
+
await fs4.mkdir(path5.dirname(absolutePath), { recursive: true });
|
|
362
|
+
await fs4.writeFile(absolutePath, serializeTask(record), "utf8");
|
|
311
363
|
const config = await readConfig(rootDir);
|
|
312
364
|
const filteredOrder = config.order.filter((item) => item !== relativePath);
|
|
313
365
|
await saveOrder(rootDir, [relativePath, ...filteredOrder]);
|
|
@@ -315,7 +367,7 @@ async function createTask(rootDir, input) {
|
|
|
315
367
|
}
|
|
316
368
|
async function updateTask(rootDir, currentPath, input) {
|
|
317
369
|
const normalizedCurrentPath = ensureMarkdownExtension(normalizeRelativePath(currentPath));
|
|
318
|
-
const absoluteCurrentPath =
|
|
370
|
+
const absoluteCurrentPath = path5.join(rootDir, normalizedCurrentPath);
|
|
319
371
|
let existing;
|
|
320
372
|
try {
|
|
321
373
|
existing = await parseTask(rootDir, normalizedCurrentPath);
|
|
@@ -329,12 +381,13 @@ async function updateTask(rootDir, currentPath, input) {
|
|
|
329
381
|
if (input.baseUpdatedAt && existing.frontmatter.updatedAt !== input.baseUpdatedAt) {
|
|
330
382
|
throw new ConflictError("The task changed on disk. Reload before saving.");
|
|
331
383
|
}
|
|
384
|
+
const status = ensureRequiredStatus(input.status);
|
|
332
385
|
const nextPath = input.path?.trim() ? await ensureDirectoryForFile(rootDir, input.path) : normalizedCurrentPath;
|
|
333
|
-
const absoluteNextPath =
|
|
386
|
+
const absoluteNextPath = path5.join(rootDir, nextPath);
|
|
334
387
|
if (nextPath !== normalizedCurrentPath) {
|
|
335
388
|
let targetExists = false;
|
|
336
389
|
try {
|
|
337
|
-
await
|
|
390
|
+
await fs4.access(absoluteNextPath);
|
|
338
391
|
targetExists = true;
|
|
339
392
|
} catch (error) {
|
|
340
393
|
const maybeError = error;
|
|
@@ -355,15 +408,15 @@ async function updateTask(rootDir, currentPath, input) {
|
|
|
355
408
|
frontmatter: {
|
|
356
409
|
title: input.title.trim(),
|
|
357
410
|
priority: input.priority,
|
|
358
|
-
status
|
|
411
|
+
status,
|
|
359
412
|
createdAt: existing.frontmatter.createdAt,
|
|
360
413
|
updatedAt: asUtcISOString(/* @__PURE__ */ new Date())
|
|
361
414
|
}
|
|
362
415
|
};
|
|
363
|
-
await
|
|
416
|
+
await fs4.writeFile(absoluteCurrentPath, serializeTask(record), "utf8");
|
|
364
417
|
if (nextPath !== normalizedCurrentPath) {
|
|
365
|
-
await
|
|
366
|
-
await
|
|
418
|
+
await fs4.mkdir(path5.dirname(absoluteNextPath), { recursive: true });
|
|
419
|
+
await fs4.rename(absoluteCurrentPath, absoluteNextPath);
|
|
367
420
|
}
|
|
368
421
|
if (nextPath !== normalizedCurrentPath) {
|
|
369
422
|
const config = await readConfig(rootDir);
|
|
@@ -382,9 +435,9 @@ async function updateTask(rootDir, currentPath, input) {
|
|
|
382
435
|
}
|
|
383
436
|
async function deleteTask(rootDir, relativePath) {
|
|
384
437
|
const normalizedPath = ensureMarkdownExtension(normalizeRelativePath(relativePath));
|
|
385
|
-
const absolutePath =
|
|
438
|
+
const absolutePath = path5.join(rootDir, normalizedPath);
|
|
386
439
|
try {
|
|
387
|
-
await
|
|
440
|
+
await fs4.unlink(absolutePath);
|
|
388
441
|
} catch (error) {
|
|
389
442
|
const maybeError = error;
|
|
390
443
|
if (maybeError.code === "ENOENT") {
|
|
@@ -400,7 +453,7 @@ async function deleteTask(rootDir, relativePath) {
|
|
|
400
453
|
}
|
|
401
454
|
async function patchTaskFields(rootDir, currentPath, input) {
|
|
402
455
|
const normalizedCurrentPath = ensureMarkdownExtension(normalizeRelativePath(currentPath));
|
|
403
|
-
const absoluteCurrentPath =
|
|
456
|
+
const absoluteCurrentPath = path5.join(rootDir, normalizedCurrentPath);
|
|
404
457
|
let existing;
|
|
405
458
|
try {
|
|
406
459
|
existing = await parseTask(rootDir, normalizedCurrentPath);
|
|
@@ -411,8 +464,8 @@ async function patchTaskFields(rootDir, currentPath, input) {
|
|
|
411
464
|
}
|
|
412
465
|
throw error;
|
|
413
466
|
}
|
|
414
|
-
const priority = input.priority &&
|
|
415
|
-
const status = input.status &&
|
|
467
|
+
const priority = input.priority && isValidPriority(input.priority) ? input.priority : existing.frontmatter.priority;
|
|
468
|
+
const status = input.status && isValidStatus(input.status) ? input.status : existing.frontmatter.status;
|
|
416
469
|
if (priority === existing.frontmatter.priority && status === existing.frontmatter.status) {
|
|
417
470
|
return existing;
|
|
418
471
|
}
|
|
@@ -429,19 +482,13 @@ async function patchTaskFields(rootDir, currentPath, input) {
|
|
|
429
482
|
updatedAt: asUtcISOString(/* @__PURE__ */ new Date())
|
|
430
483
|
}
|
|
431
484
|
};
|
|
432
|
-
await
|
|
485
|
+
await fs4.writeFile(absoluteCurrentPath, serializeTask(record), "utf8");
|
|
433
486
|
return parseTask(rootDir, normalizedCurrentPath);
|
|
434
487
|
}
|
|
435
|
-
function parseOrderPayload(input) {
|
|
436
|
-
if (!Array.isArray(input)) {
|
|
437
|
-
throw new ValidationError("Order payload must be an array.");
|
|
438
|
-
}
|
|
439
|
-
return input.map((item) => ensureMarkdownExtension(normalizeRelativePath(String(item))));
|
|
440
|
-
}
|
|
441
488
|
|
|
442
489
|
// src/commandExecutor.ts
|
|
443
490
|
import { spawn } from "child_process";
|
|
444
|
-
import
|
|
491
|
+
import path6 from "path";
|
|
445
492
|
var TIMEOUT_MS = 3e4;
|
|
446
493
|
var VARIABLE_PATTERN = /\$\{?(TASK_TITLE|TASK_FILEPATH|TASK_BODY)\}?/g;
|
|
447
494
|
function substituteVariables(command, vars) {
|
|
@@ -451,7 +498,7 @@ async function executeCommandPipeline(rootDir, steps, task) {
|
|
|
451
498
|
if (steps.length === 0) {
|
|
452
499
|
return { stdout: "", stderr: "", exitCode: 0, duration: 0 };
|
|
453
500
|
}
|
|
454
|
-
const absoluteFilePath =
|
|
501
|
+
const absoluteFilePath = path6.resolve(rootDir, task.path);
|
|
455
502
|
const vars = {
|
|
456
503
|
TASK_TITLE: task.frontmatter.title,
|
|
457
504
|
TASK_FILEPATH: absoluteFilePath,
|
|
@@ -529,7 +576,7 @@ async function executeCommandPipeline(rootDir, steps, task) {
|
|
|
529
576
|
|
|
530
577
|
// src/server.ts
|
|
531
578
|
var __filename = fileURLToPath(import.meta.url);
|
|
532
|
-
var __dirname =
|
|
579
|
+
var __dirname = path7.dirname(__filename);
|
|
533
580
|
function resolveClientDir(explicitClientDir) {
|
|
534
581
|
if (explicitClientDir === null) {
|
|
535
582
|
return null;
|
|
@@ -537,7 +584,7 @@ function resolveClientDir(explicitClientDir) {
|
|
|
537
584
|
if (explicitClientDir) {
|
|
538
585
|
return explicitClientDir;
|
|
539
586
|
}
|
|
540
|
-
return
|
|
587
|
+
return path7.resolve(__dirname, "client");
|
|
541
588
|
}
|
|
542
589
|
function sendJsonError(reply, error) {
|
|
543
590
|
if (error instanceof ValidationError) {
|
|
@@ -700,18 +747,18 @@ async function createServer(options) {
|
|
|
700
747
|
});
|
|
701
748
|
const watcher = chokidar.watch(options.rootDir, {
|
|
702
749
|
ignoreInitial: true,
|
|
703
|
-
ignored: (watchPath) => watchPath.includes(`${
|
|
750
|
+
ignored: (watchPath) => watchPath.includes(`${path7.sep}.git`) || watchPath.includes(`${path7.sep}node_modules`)
|
|
704
751
|
});
|
|
705
752
|
watcher.on("all", (eventName, changedPath) => {
|
|
706
753
|
const isMarkdown = changedPath.endsWith(".md") || changedPath.endsWith(".markdown");
|
|
707
|
-
const isConfigFile =
|
|
754
|
+
const isConfigFile = path7.basename(changedPath) === ".md-task-viewer.json";
|
|
708
755
|
if (!isMarkdown && !isConfigFile) {
|
|
709
756
|
return;
|
|
710
757
|
}
|
|
711
758
|
const payload = JSON.stringify({
|
|
712
759
|
type: "tasks-changed",
|
|
713
760
|
eventName,
|
|
714
|
-
path:
|
|
761
|
+
path: path7.relative(options.rootDir, changedPath)
|
|
715
762
|
});
|
|
716
763
|
for (const listener of listeners) {
|
|
717
764
|
listener.send(payload);
|