roadmap-skill 0.1.4 → 0.2.3
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/CHANGELOG.md +29 -0
- package/README.md +111 -54
- package/README.zh.md +387 -0
- package/dist/index.js +499 -518
- package/dist/index.js.map +1 -1
- package/dist/web/app/assets/main-DZvNWN99.js +9 -0
- package/dist/web/app/index.html +15 -1
- package/dist/web/server.js +166 -150
- package/dist/web/server.js.map +1 -1
- package/package.json +3 -3
- package/dist/web/app/assets/main-Bx7N5Sfv.js +0 -9
package/dist/web/app/index.html
CHANGED
|
@@ -15,8 +15,22 @@
|
|
|
15
15
|
background-image: radial-gradient(#e2e8f0 1px, transparent 1px);
|
|
16
16
|
background-size: 24px 24px;
|
|
17
17
|
}
|
|
18
|
+
@keyframes fadeIn {
|
|
19
|
+
from { opacity: 0; }
|
|
20
|
+
to { opacity: 1; }
|
|
21
|
+
}
|
|
22
|
+
@keyframes slideUp {
|
|
23
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
24
|
+
to { opacity: 1; transform: translateY(0); }
|
|
25
|
+
}
|
|
26
|
+
.animate-fadeIn {
|
|
27
|
+
animation: fadeIn 0.2s ease-out;
|
|
28
|
+
}
|
|
29
|
+
.animate-slideUp {
|
|
30
|
+
animation: slideUp 0.3s ease-out;
|
|
31
|
+
}
|
|
18
32
|
</style>
|
|
19
|
-
<script type="module" crossorigin src="./assets/main-
|
|
33
|
+
<script type="module" crossorigin src="./assets/main-DZvNWN99.js"></script>
|
|
20
34
|
</head>
|
|
21
35
|
<body class="roadmap-grid">
|
|
22
36
|
<div id="root"></div>
|
package/dist/web/server.js
CHANGED
|
@@ -257,6 +257,9 @@ var ProjectStorage = class {
|
|
|
257
257
|
if (filters.searchText && !task.title.toLowerCase().includes(filters.searchText.toLowerCase()) && !task.description.toLowerCase().includes(filters.searchText.toLowerCase())) {
|
|
258
258
|
continue;
|
|
259
259
|
}
|
|
260
|
+
if (filters.includeCompleted === false && task.status === "done") {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
260
263
|
results.push({ task, project: data.project });
|
|
261
264
|
}
|
|
262
265
|
} catch {
|
|
@@ -270,165 +273,178 @@ var storage = new ProjectStorage();
|
|
|
270
273
|
|
|
271
274
|
// src/web/server.ts
|
|
272
275
|
function createServer(port = 7860) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
});
|
|
283
|
-
app.get("/api/projects/:id", async (req, res) => {
|
|
284
|
-
try {
|
|
285
|
-
const project = await storage.readProject(req.params.id);
|
|
286
|
-
if (!project) {
|
|
287
|
-
res.status(404).json({ error: "Project not found" });
|
|
288
|
-
return;
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
const app = express();
|
|
278
|
+
app.use(express.json());
|
|
279
|
+
app.get("/api/projects", async (_req, res) => {
|
|
280
|
+
try {
|
|
281
|
+
const projects = await storage.listProjects();
|
|
282
|
+
res.json(projects);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
res.status(500).json({ error: error.message });
|
|
289
285
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
res.status(500).json({ error: error.message });
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
app.post("/api/projects", async (req, res) => {
|
|
305
|
-
try {
|
|
306
|
-
const project = await storage.createProject(req.body);
|
|
307
|
-
res.json({ success: true, data: project });
|
|
308
|
-
} catch (error) {
|
|
309
|
-
res.status(500).json({ error: error.message });
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
app.put("/api/projects", async (req, res) => {
|
|
313
|
-
try {
|
|
314
|
-
const { projectId, ...updateData } = req.body;
|
|
315
|
-
const project = await storage.updateProject(projectId, updateData);
|
|
316
|
-
res.json({ success: true, data: project });
|
|
317
|
-
} catch (error) {
|
|
318
|
-
res.status(500).json({ error: error.message });
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
app.delete("/api/projects", async (req, res) => {
|
|
322
|
-
try {
|
|
323
|
-
const { projectId } = req.query;
|
|
324
|
-
await storage.deleteProject(projectId);
|
|
325
|
-
res.json({ success: true });
|
|
326
|
-
} catch (error) {
|
|
327
|
-
res.status(500).json({ error: error.message });
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
app.post("/api/tasks", async (req, res) => {
|
|
331
|
-
try {
|
|
332
|
-
const { projectId, ...taskData } = req.body;
|
|
333
|
-
const projectData = await storage.readProject(projectId);
|
|
334
|
-
if (!projectData) {
|
|
335
|
-
res.status(404).json({ error: "Project not found" });
|
|
336
|
-
return;
|
|
286
|
+
});
|
|
287
|
+
app.get("/api/projects/:id", async (req, res) => {
|
|
288
|
+
try {
|
|
289
|
+
const project = await storage.readProject(req.params.id);
|
|
290
|
+
if (!project) {
|
|
291
|
+
res.status(404).json({ error: "Project not found" });
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
res.json(project);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
res.status(500).json({ error: error.message });
|
|
337
297
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
updatedAt: now,
|
|
350
|
-
completedAt: null
|
|
351
|
-
};
|
|
352
|
-
projectData.tasks.push(task);
|
|
353
|
-
projectData.project.updatedAt = now;
|
|
354
|
-
const filePath = storage.getFilePath(projectId);
|
|
355
|
-
const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
|
|
356
|
-
await writeJsonFile2(filePath, projectData);
|
|
357
|
-
res.json({ success: true, data: task });
|
|
358
|
-
} catch (error) {
|
|
359
|
-
res.status(500).json({ error: error.message });
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
app.put("/api/tasks", async (req, res) => {
|
|
363
|
-
try {
|
|
364
|
-
const { projectId, taskId, ...updateData } = req.body;
|
|
365
|
-
const projectData = await storage.readProject(projectId);
|
|
366
|
-
if (!projectData) {
|
|
367
|
-
res.status(404).json({ error: "Project not found" });
|
|
368
|
-
return;
|
|
298
|
+
});
|
|
299
|
+
app.get("/api/tasks", async (req, res) => {
|
|
300
|
+
try {
|
|
301
|
+
const filters = { ...req.query };
|
|
302
|
+
if (filters.includeCompleted !== void 0) {
|
|
303
|
+
filters.includeCompleted = filters.includeCompleted === "true";
|
|
304
|
+
}
|
|
305
|
+
const tasks = await storage.searchTasks(filters);
|
|
306
|
+
res.json(tasks);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
res.status(500).json({ error: error.message });
|
|
369
309
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
310
|
+
});
|
|
311
|
+
app.post("/api/projects", async (req, res) => {
|
|
312
|
+
try {
|
|
313
|
+
const project = await storage.createProject(req.body);
|
|
314
|
+
res.json({ success: true, data: project });
|
|
315
|
+
} catch (error) {
|
|
316
|
+
res.status(500).json({ error: error.message });
|
|
374
317
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
...
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
318
|
+
});
|
|
319
|
+
app.put("/api/projects", async (req, res) => {
|
|
320
|
+
try {
|
|
321
|
+
const { projectId, ...updateData } = req.body;
|
|
322
|
+
const project = await storage.updateProject(projectId, updateData);
|
|
323
|
+
res.json({ success: true, data: project });
|
|
324
|
+
} catch (error) {
|
|
325
|
+
res.status(500).json({ error: error.message });
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
app.delete("/api/projects", async (req, res) => {
|
|
329
|
+
try {
|
|
330
|
+
const { projectId } = req.query;
|
|
331
|
+
await storage.deleteProject(projectId);
|
|
332
|
+
res.json({ success: true });
|
|
333
|
+
} catch (error) {
|
|
334
|
+
res.status(500).json({ error: error.message });
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
app.post("/api/tasks", async (req, res) => {
|
|
338
|
+
try {
|
|
339
|
+
const { projectId, ...taskData } = req.body;
|
|
340
|
+
const projectData = await storage.readProject(projectId);
|
|
341
|
+
if (!projectData) {
|
|
342
|
+
res.status(404).json({ error: "Project not found" });
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
346
|
+
const task = {
|
|
347
|
+
id: `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
348
|
+
projectId,
|
|
349
|
+
...taskData,
|
|
350
|
+
status: taskData.status || "todo",
|
|
351
|
+
priority: taskData.priority || "medium",
|
|
352
|
+
tags: taskData.tags || [],
|
|
353
|
+
dueDate: taskData.dueDate || null,
|
|
354
|
+
assignee: taskData.assignee || null,
|
|
355
|
+
createdAt: now,
|
|
356
|
+
updatedAt: now,
|
|
357
|
+
completedAt: null
|
|
358
|
+
};
|
|
359
|
+
projectData.tasks.push(task);
|
|
360
|
+
projectData.project.updatedAt = now;
|
|
361
|
+
const filePath = storage.getFilePath(projectId);
|
|
362
|
+
const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
|
|
363
|
+
await writeJsonFile2(filePath, projectData);
|
|
364
|
+
res.json({ success: true, data: task });
|
|
365
|
+
} catch (error) {
|
|
366
|
+
res.status(500).json({ error: error.message });
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
app.put("/api/tasks", async (req, res) => {
|
|
370
|
+
try {
|
|
371
|
+
const { projectId, taskId, ...updateData } = req.body;
|
|
372
|
+
const projectData = await storage.readProject(projectId);
|
|
373
|
+
if (!projectData) {
|
|
374
|
+
res.status(404).json({ error: "Project not found" });
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);
|
|
378
|
+
if (taskIndex === -1) {
|
|
379
|
+
res.status(404).json({ error: "Task not found" });
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
383
|
+
const existingTask = projectData.tasks[taskIndex];
|
|
384
|
+
const updatedTask = {
|
|
385
|
+
...existingTask,
|
|
386
|
+
...updateData,
|
|
387
|
+
id: existingTask.id,
|
|
388
|
+
projectId: existingTask.projectId,
|
|
389
|
+
createdAt: existingTask.createdAt,
|
|
390
|
+
updatedAt: now,
|
|
391
|
+
completedAt: updateData.status === "done" && existingTask.status !== "done" ? now : updateData.status && updateData.status !== "done" ? null : existingTask.completedAt
|
|
392
|
+
};
|
|
393
|
+
projectData.tasks[taskIndex] = updatedTask;
|
|
394
|
+
projectData.project.updatedAt = now;
|
|
395
|
+
const filePath = storage.getFilePath(projectId);
|
|
396
|
+
const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
|
|
397
|
+
await writeJsonFile2(filePath, projectData);
|
|
398
|
+
res.json({ success: true, data: updatedTask });
|
|
399
|
+
} catch (error) {
|
|
400
|
+
res.status(500).json({ error: error.message });
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
app.delete("/api/tasks", async (req, res) => {
|
|
404
|
+
try {
|
|
405
|
+
const { projectId, taskId } = req.query;
|
|
406
|
+
const projectData = await storage.readProject(projectId);
|
|
407
|
+
if (!projectData) {
|
|
408
|
+
res.status(404).json({ error: "Project not found" });
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);
|
|
412
|
+
if (taskIndex === -1) {
|
|
413
|
+
res.status(404).json({ error: "Task not found" });
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
projectData.tasks.splice(taskIndex, 1);
|
|
417
|
+
projectData.project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
418
|
+
const filePath = storage.getFilePath(projectId);
|
|
419
|
+
const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
|
|
420
|
+
await writeJsonFile2(filePath, projectData);
|
|
421
|
+
res.json({ success: true });
|
|
422
|
+
} catch (error) {
|
|
423
|
+
res.status(500).json({ error: error.message });
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
const distPath = path4.join(process.cwd(), "dist", "web", "app");
|
|
427
|
+
app.use(express.static(distPath));
|
|
428
|
+
app.get("*", (req, res) => {
|
|
429
|
+
if (req.path.startsWith("/api")) {
|
|
430
|
+
res.status(404).json({ error: "API not found" });
|
|
402
431
|
return;
|
|
403
432
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
433
|
+
res.sendFile(path4.join(distPath, "index.html"));
|
|
434
|
+
});
|
|
435
|
+
const server = app.listen(port, "127.0.0.1");
|
|
436
|
+
server.once("listening", () => {
|
|
437
|
+
console.log(`Web interface server running at http://localhost:${port}`);
|
|
438
|
+
resolve(server);
|
|
439
|
+
});
|
|
440
|
+
server.once("error", (error) => {
|
|
441
|
+
if (error.code === "EADDRINUSE") {
|
|
442
|
+
reject(new Error(`Port ${port} is already in use`));
|
|
407
443
|
return;
|
|
408
444
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const filePath = storage.getFilePath(projectId);
|
|
412
|
-
const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
|
|
413
|
-
await writeJsonFile2(filePath, projectData);
|
|
414
|
-
res.json({ success: true });
|
|
415
|
-
} catch (error) {
|
|
416
|
-
res.status(500).json({ error: error.message });
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
const distPath = path4.join(process.cwd(), "dist", "web", "app");
|
|
420
|
-
app.use(express.static(distPath));
|
|
421
|
-
app.get("*", (req, res) => {
|
|
422
|
-
if (req.path.startsWith("/api")) {
|
|
423
|
-
res.status(404).json({ error: "API not found" });
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
res.sendFile(path4.join(distPath, "index.html"));
|
|
427
|
-
});
|
|
428
|
-
const server = app.listen(port, "0.0.0.0", () => {
|
|
429
|
-
console.log(`Web interface server running at http://0.0.0.0:${port}`);
|
|
445
|
+
reject(error);
|
|
446
|
+
});
|
|
430
447
|
});
|
|
431
|
-
return server;
|
|
432
448
|
}
|
|
433
449
|
export {
|
|
434
450
|
createServer
|
package/dist/web/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../node_modules/tsup/assets/esm_shims.js","../../src/utils/file-helpers.ts","../../src/web/server.ts","../../src/storage/index.ts","../../src/utils/path-helpers.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import * as fs from 'fs/promises';\n\n/**\n * Read and parse a JSON file\n * @param filePath - Path to the JSON file\n * @returns Parsed JSON data\n * @throws Error if file cannot be read or parsed\n */\nexport async function readJsonFile<T>(filePath: string): Promise<T> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to read JSON file ${filePath}: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Write data to a JSON file\n * @param filePath - Path to the JSON file\n * @param data - Data to serialize and write\n * @throws Error if file cannot be written\n */\nexport async function writeJsonFile<T>(filePath: string, data: T): Promise<void> {\n try {\n const content = JSON.stringify(data, null, 2);\n await fs.writeFile(filePath, content, 'utf-8');\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to write JSON file ${filePath}: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Ensure a directory exists, creating it recursively if needed\n * @param dirPath - Path to the directory\n * @throws Error if directory cannot be created\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n try {\n await fs.mkdir(dirPath, { recursive: true });\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);\n }\n throw error;\n }\n}\n","import express from 'express';\nimport * as path from 'path';\nimport { storage } from '../storage/index.js';\n\nexport function createServer(port: number = 7860) {\n const app = express();\n\n app.use(express.json());\n\n app.get('/api/projects', async (_req, res) => {\n try {\n const projects = await storage.listProjects();\n res.json(projects);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/projects/:id', async (req, res) => {\n try {\n const project = await storage.readProject(req.params.id);\n if (!project) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n res.json(project);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/tasks', async (req, res) => {\n try {\n const filters = req.query;\n const tasks = await storage.searchTasks(filters as any);\n res.json(tasks);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/projects', async (req, res) => {\n try {\n const project = await storage.createProject(req.body);\n res.json({ success: true, data: project });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/projects', async (req, res) => {\n try {\n const { projectId, ...updateData } = req.body;\n const project = await storage.updateProject(projectId, updateData);\n res.json({ success: true, data: project });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/projects', async (req, res) => {\n try {\n const { projectId } = req.query;\n await storage.deleteProject(projectId as string);\n res.json({ success: true });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/tasks', async (req, res) => {\n try {\n const { projectId, ...taskData } = req.body;\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n const now = new Date().toISOString();\n const task = {\n id: `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,\n projectId,\n ...taskData,\n status: taskData.status || 'todo',\n priority: taskData.priority || 'medium',\n tags: taskData.tags || [],\n dueDate: taskData.dueDate || null,\n assignee: taskData.assignee || null,\n createdAt: now,\n updatedAt: now,\n completedAt: null,\n };\n projectData.tasks.push(task);\n projectData.project.updatedAt = now;\n const filePath = storage.getFilePath(projectId);\n const { writeJsonFile } = await import('../utils/file-helpers.js');\n await writeJsonFile(filePath, projectData);\n res.json({ success: true, data: task });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/tasks', async (req, res) => {\n try {\n const { projectId, taskId, ...updateData } = req.body;\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n const taskIndex = projectData.tasks.findIndex((t: any) => t.id === taskId);\n if (taskIndex === -1) {\n res.status(404).json({ error: 'Task not found' });\n return;\n }\n const now = new Date().toISOString();\n const existingTask = projectData.tasks[taskIndex];\n const updatedTask = {\n ...existingTask,\n ...updateData,\n id: existingTask.id,\n projectId: existingTask.projectId,\n createdAt: existingTask.createdAt,\n updatedAt: now,\n completedAt: updateData.status === 'done' && existingTask.status !== 'done'\n ? now\n : updateData.status && updateData.status !== 'done'\n ? null\n : existingTask.completedAt,\n };\n projectData.tasks[taskIndex] = updatedTask;\n projectData.project.updatedAt = now;\n const filePath = storage.getFilePath(projectId);\n const { writeJsonFile } = await import('../utils/file-helpers.js');\n await writeJsonFile(filePath, projectData);\n res.json({ success: true, data: updatedTask });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/tasks', async (req, res) => {\n try {\n const { projectId, taskId } = req.query;\n const projectData = await storage.readProject(projectId as string);\n if (!projectData) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n const taskIndex = projectData.tasks.findIndex((t: any) => t.id === taskId);\n if (taskIndex === -1) {\n res.status(404).json({ error: 'Task not found' });\n return;\n }\n projectData.tasks.splice(taskIndex, 1);\n projectData.project.updatedAt = new Date().toISOString();\n const filePath = storage.getFilePath(projectId as string);\n const { writeJsonFile } = await import('../utils/file-helpers.js');\n await writeJsonFile(filePath, projectData);\n res.json({ success: true });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n const distPath = path.join(process.cwd(), 'dist', 'web', 'app');\n app.use(express.static(distPath));\n\n app.get('*', (req, res) => {\n if (req.path.startsWith('/api')) {\n res.status(404).json({ error: 'API not found' });\n return;\n }\n res.sendFile(path.join(distPath, 'index.html'));\n });\n\n const server = app.listen(port, '0.0.0.0', () => {\n console.log(`Web interface server running at http://0.0.0.0:${port}`);\n });\n\n return server;\n}\n","import * as path from 'path';\nimport type {\n Project,\n ProjectData,\n Task,\n TaskSearchFilters,\n CreateProjectInput,\n UpdateProjectInput,\n} from '../models/index.js';\nimport { getStorageDir } from '../utils/path-helpers.js';\nimport { readJsonFile, writeJsonFile, ensureDir } from '../utils/file-helpers.js';\n\n/**\n * Storage class for managing roadmap-skill projects\n * Projects are stored as individual JSON files in ~/.roadmap-skill/projects/\n */\nexport class ProjectStorage {\n private storageDir: string;\n\n constructor() {\n this.storageDir = getStorageDir();\n }\n\n /**\n * Ensure the storage directory exists\n */\n async ensureDirectory(): Promise<void> {\n await ensureDir(this.storageDir);\n }\n\n /**\n * Get the file path for a project\n * @param projectId - The project ID\n * @returns Full path to the project JSON file\n */\n getFilePath(projectId: string): string {\n return path.join(this.storageDir, `${projectId}.json`);\n }\n\n /**\n * Create a new project\n * @param input - Project creation data\n * @returns The created project data\n */\n async createProject(input: CreateProjectInput): Promise<ProjectData> {\n await this.ensureDirectory();\n\n const now = new Date().toISOString();\n const projectId = `proj_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n const project: Project = {\n id: projectId,\n name: input.name,\n description: input.description,\n projectType: input.projectType,\n status: 'active',\n startDate: input.startDate,\n targetDate: input.targetDate,\n createdAt: now,\n updatedAt: now,\n };\n\n const projectData: ProjectData = {\n version: 1,\n project,\n milestones: [],\n tasks: [],\n tags: [],\n };\n\n const filePath = this.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return projectData;\n }\n\n /**\n * Read a project by ID\n * @param projectId - The project ID\n * @returns The project data or null if not found\n */\n async readProject(projectId: string): Promise<ProjectData | null> {\n try {\n const filePath = this.getFilePath(projectId);\n return await readJsonFile<ProjectData>(filePath);\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Update an existing project\n * @param projectId - The project ID\n * @param input - Project update data\n * @returns The updated project data or null if not found\n */\n async updateProject(\n projectId: string,\n input: UpdateProjectInput\n ): Promise<ProjectData | null> {\n const projectData = await this.readProject(projectId);\n if (!projectData) {\n return null;\n }\n\n const now = new Date().toISOString();\n\n projectData.project = {\n ...projectData.project,\n ...input,\n updatedAt: now,\n };\n\n const filePath = this.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return projectData;\n }\n\n /**\n * Delete a project by ID\n * @param projectId - The project ID\n * @returns True if deleted, false if not found\n */\n async deleteProject(projectId: string): Promise<boolean> {\n try {\n const filePath = this.getFilePath(projectId);\n const fs = await import('fs/promises');\n await fs.unlink(filePath);\n return true;\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * List all projects sorted by updatedAt (descending)\n * @returns Array of project summaries (project + metadata)\n */\n async listProjects(): Promise<Array<{ project: Project; taskCount: number; milestoneCount: number }>> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const projects: Array<{ project: Project; taskCount: number; milestoneCount: number }> = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n projects.push({\n project: data.project,\n taskCount: data.tasks.length,\n milestoneCount: data.milestones.length,\n });\n } catch {\n // Skip invalid files\n continue;\n }\n }\n\n // Sort by updatedAt descending\n return projects.sort(\n (a, b) => new Date(b.project.updatedAt).getTime() - new Date(a.project.updatedAt).getTime()\n );\n }\n\n /**\n * Search tasks across all projects with filters\n * @param filters - Search filters\n * @returns Array of matching tasks with project context\n */\n async searchTasks(\n filters: TaskSearchFilters\n ): Promise<Array<{ task: Task; project: Project }>> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const results: Array<{ task: Task; project: Project }> = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n\n // Skip if project filter doesn't match\n if (filters.projectId && data.project.id !== filters.projectId) {\n continue;\n }\n\n for (const task of data.tasks) {\n // Apply filters\n if (filters.status && task.status !== filters.status) {\n continue;\n }\n if (filters.priority && task.priority !== filters.priority) {\n continue;\n }\n if (filters.assignee && task.assignee !== filters.assignee) {\n continue;\n }\n if (filters.dueBefore && task.dueDate && task.dueDate > filters.dueBefore) {\n continue;\n }\n if (filters.dueAfter && task.dueDate && task.dueDate < filters.dueAfter) {\n continue;\n }\n if (\n filters.tags &&\n filters.tags.length > 0 &&\n !filters.tags.some((tag) => task.tags.includes(tag))\n ) {\n continue;\n }\n if (\n filters.searchText &&\n !task.title.toLowerCase().includes(filters.searchText.toLowerCase()) &&\n !task.description.toLowerCase().includes(filters.searchText.toLowerCase())\n ) {\n continue;\n }\n\n results.push({ task, project: data.project });\n }\n } catch {\n // Skip invalid files\n continue;\n }\n }\n\n return results;\n }\n}\n\n// Export singleton instance\nexport const storage = new ProjectStorage();\n","import * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Get the storage directory for roadmap-skill projects\n * Returns: ~/.roadmap-skill/projects\n */\nexport function getStorageDir(): string {\n const homeDir = os.homedir();\n return path.join(homeDir, '.roadmap-skill', 'projects');\n}\n"],"mappings":";;;;;;;;;;;;AACA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAF9B;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAAY,QAAQ;AAQpB,eAAsB,aAAgB,UAA8B;AAClE,MAAI;AACF,UAAM,UAAU,MAAS,YAAS,UAAU,OAAO;AACnD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,YAAM,IAAI,MAAM,4BAA4B,QAAQ,KAAK,MAAM,OAAO,EAAE;AAAA,IAC1E;AACA,UAAM;AAAA,EACR;AACF;AAQA,eAAsB,cAAiB,UAAkB,MAAwB;AAC/E,MAAI;AACF,UAAM,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC;AAC5C,UAAS,aAAU,UAAU,SAAS,OAAO;AAAA,EAC/C,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,YAAM,IAAI,MAAM,6BAA6B,QAAQ,KAAK,MAAM,OAAO,EAAE;AAAA,IAC3E;AACA,UAAM;AAAA,EACR;AACF;AAOA,eAAsB,UAAU,SAAgC;AAC9D,MAAI;AACF,UAAS,SAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,EAC7C,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,YAAM,IAAI,MAAM,8BAA8B,OAAO,KAAK,MAAM,OAAO,EAAE;AAAA,IAC3E;AACA,UAAM;AAAA,EACR;AACF;AApDA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA,OAAO,aAAa;AACpB,YAAYA,WAAU;;;ACDtB;AAAA,YAAYC,WAAU;;;ACAtB;AAAA,YAAY,QAAQ;AACpB,YAAYC,WAAU;AAMf,SAAS,gBAAwB;AACtC,QAAM,UAAa,WAAQ;AAC3B,SAAY,WAAK,SAAS,kBAAkB,UAAU;AACxD;;;ADAA;AAMO,IAAM,iBAAN,MAAqB;AAAA,EAClB;AAAA,EAER,cAAc;AACZ,SAAK,aAAa,cAAc;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAiC;AACrC,UAAM,UAAU,KAAK,UAAU;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,WAA2B;AACrC,WAAY,WAAK,KAAK,YAAY,GAAG,SAAS,OAAO;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cAAc,OAAiD;AACnE,UAAM,KAAK,gBAAgB;AAE3B,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,YAAY,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAElF,UAAM,UAAmB;AAAA,MACvB,IAAI;AAAA,MACJ,MAAM,MAAM;AAAA,MACZ,aAAa,MAAM;AAAA,MACnB,aAAa,MAAM;AAAA,MACnB,QAAQ;AAAA,MACR,WAAW,MAAM;AAAA,MACjB,YAAY,MAAM;AAAA,MAClB,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAEA,UAAM,cAA2B;AAAA,MAC/B,SAAS;AAAA,MACT;AAAA,MACA,YAAY,CAAC;AAAA,MACb,OAAO,CAAC;AAAA,MACR,MAAM,CAAC;AAAA,IACT;AAEA,UAAM,WAAW,KAAK,YAAY,SAAS;AAC3C,UAAM,cAAc,UAAU,WAAW;AAEzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAY,WAAgD;AAChE,QAAI;AACF,YAAM,WAAW,KAAK,YAAY,SAAS;AAC3C,aAAO,MAAM,aAA0B,QAAQ;AAAA,IACjD,SAAS,OAAO;AACd,UAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,QAAQ,GAAG;AAC9D,eAAO;AAAA,MACT;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cACJ,WACA,OAC6B;AAC7B,UAAM,cAAc,MAAM,KAAK,YAAY,SAAS;AACpD,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA,IACT;AAEA,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AAEnC,gBAAY,UAAU;AAAA,MACpB,GAAG,YAAY;AAAA,MACf,GAAG;AAAA,MACH,WAAW;AAAA,IACb;AAEA,UAAM,WAAW,KAAK,YAAY,SAAS;AAC3C,UAAM,cAAc,UAAU,WAAW;AAEzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cAAc,WAAqC;AACvD,QAAI;AACF,YAAM,WAAW,KAAK,YAAY,SAAS;AAC3C,YAAMC,MAAK,MAAM,OAAO,aAAa;AACrC,YAAMA,IAAG,OAAO,QAAQ;AACxB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,QAAQ,GAAG;AAC9D,eAAO;AAAA,MACT;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAgG;AACpG,UAAM,KAAK,gBAAgB;AAE3B,UAAMA,MAAK,MAAM,OAAO,aAAa;AACrC,UAAM,QAAQ,MAAMA,IAAG,QAAQ,KAAK,UAAU;AAC9C,UAAM,YAAY,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAEzD,UAAM,WAAmF,CAAC;AAE1F,eAAW,QAAQ,WAAW;AAC5B,UAAI;AACF,cAAM,WAAgB,WAAK,KAAK,YAAY,IAAI;AAChD,cAAM,OAAO,MAAM,aAA0B,QAAQ;AACrD,iBAAS,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,UACd,WAAW,KAAK,MAAM;AAAA,UACtB,gBAAgB,KAAK,WAAW;AAAA,QAClC,CAAC;AAAA,MACH,QAAQ;AAEN;AAAA,MACF;AAAA,IACF;AAGA,WAAO,SAAS;AAAA,MACd,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,QAAQ,SAAS,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,QAAQ,SAAS,EAAE,QAAQ;AAAA,IAC5F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YACJ,SACkD;AAClD,UAAM,KAAK,gBAAgB;AAE3B,UAAMA,MAAK,MAAM,OAAO,aAAa;AACrC,UAAM,QAAQ,MAAMA,IAAG,QAAQ,KAAK,UAAU;AAC9C,UAAM,YAAY,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAEzD,UAAM,UAAmD,CAAC;AAE1D,eAAW,QAAQ,WAAW;AAC5B,UAAI;AACF,cAAM,WAAgB,WAAK,KAAK,YAAY,IAAI;AAChD,cAAM,OAAO,MAAM,aAA0B,QAAQ;AAGrD,YAAI,QAAQ,aAAa,KAAK,QAAQ,OAAO,QAAQ,WAAW;AAC9D;AAAA,QACF;AAEA,mBAAW,QAAQ,KAAK,OAAO;AAE7B,cAAI,QAAQ,UAAU,KAAK,WAAW,QAAQ,QAAQ;AACpD;AAAA,UACF;AACA,cAAI,QAAQ,YAAY,KAAK,aAAa,QAAQ,UAAU;AAC1D;AAAA,UACF;AACA,cAAI,QAAQ,YAAY,KAAK,aAAa,QAAQ,UAAU;AAC1D;AAAA,UACF;AACA,cAAI,QAAQ,aAAa,KAAK,WAAW,KAAK,UAAU,QAAQ,WAAW;AACzE;AAAA,UACF;AACA,cAAI,QAAQ,YAAY,KAAK,WAAW,KAAK,UAAU,QAAQ,UAAU;AACvE;AAAA,UACF;AACA,cACE,QAAQ,QACR,QAAQ,KAAK,SAAS,KACtB,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,KAAK,KAAK,SAAS,GAAG,CAAC,GACnD;AACA;AAAA,UACF;AACA,cACE,QAAQ,cACR,CAAC,KAAK,MAAM,YAAY,EAAE,SAAS,QAAQ,WAAW,YAAY,CAAC,KACnE,CAAC,KAAK,YAAY,YAAY,EAAE,SAAS,QAAQ,WAAW,YAAY,CAAC,GACzE;AACA;AAAA,UACF;AAEA,kBAAQ,KAAK,EAAE,MAAM,SAAS,KAAK,QAAQ,CAAC;AAAA,QAC9C;AAAA,MACF,QAAQ;AAEN;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAGO,IAAM,UAAU,IAAI,eAAe;;;ADlPnC,SAAS,aAAa,OAAe,MAAM;AAChD,QAAM,MAAM,QAAQ;AAEpB,MAAI,IAAI,QAAQ,KAAK,CAAC;AAEtB,MAAI,IAAI,iBAAiB,OAAO,MAAM,QAAQ;AAC5C,QAAI;AACF,YAAM,WAAW,MAAM,QAAQ,aAAa;AAC5C,UAAI,KAAK,QAAQ;AAAA,IACnB,SAAS,OAAO;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,IAAI,qBAAqB,OAAO,KAAK,QAAQ;AAC/C,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,YAAY,IAAI,OAAO,EAAE;AACvD,UAAI,CAAC,SAAS;AACZ,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;AAAA,MACF;AACA,UAAI,KAAK,OAAO;AAAA,IAClB,SAAS,OAAO;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,IAAI,cAAc,OAAO,KAAK,QAAQ;AACxC,QAAI;AACF,YAAM,UAAU,IAAI;AACpB,YAAM,QAAQ,MAAM,QAAQ,YAAY,OAAc;AACtD,UAAI,KAAK,KAAK;AAAA,IAChB,SAAS,OAAO;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,KAAK,iBAAiB,OAAO,KAAK,QAAQ;AAC5C,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,cAAc,IAAI,IAAI;AACpD,UAAI,KAAK,EAAE,SAAS,MAAM,MAAM,QAAQ,CAAC;AAAA,IAC3C,SAAS,OAAO;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,IAAI,iBAAiB,OAAO,KAAK,QAAQ;AAC3C,QAAI;AACF,YAAM,EAAE,WAAW,GAAG,WAAW,IAAI,IAAI;AACzC,YAAM,UAAU,MAAM,QAAQ,cAAc,WAAW,UAAU;AACjE,UAAI,KAAK,EAAE,SAAS,MAAM,MAAM,QAAQ,CAAC;AAAA,IAC3C,SAAS,OAAO;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,OAAO,iBAAiB,OAAO,KAAK,QAAQ;AAC9C,QAAI;AACF,YAAM,EAAE,UAAU,IAAI,IAAI;AAC1B,YAAM,QAAQ,cAAc,SAAmB;AAC/C,UAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,IAC5B,SAAS,OAAO;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,KAAK,cAAc,OAAO,KAAK,QAAQ;AACzC,QAAI;AACF,YAAM,EAAE,WAAW,GAAG,SAAS,IAAI,IAAI;AACvC,YAAM,cAAc,MAAM,QAAQ,YAAY,SAAS;AACvD,UAAI,CAAC,aAAa;AAChB,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;AAAA,MACF;AACA,YAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,YAAM,OAAO;AAAA,QACX,IAAI,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,QACpE;AAAA,QACA,GAAG;AAAA,QACH,QAAQ,SAAS,UAAU;AAAA,QAC3B,UAAU,SAAS,YAAY;AAAA,QAC/B,MAAM,SAAS,QAAQ,CAAC;AAAA,QACxB,SAAS,SAAS,WAAW;AAAA,QAC7B,UAAU,SAAS,YAAY;AAAA,QAC/B,WAAW;AAAA,QACX,WAAW;AAAA,QACX,aAAa;AAAA,MACf;AACA,kBAAY,MAAM,KAAK,IAAI;AAC3B,kBAAY,QAAQ,YAAY;AAChC,YAAM,WAAW,QAAQ,YAAY,SAAS;AAC9C,YAAM,EAAE,eAAAC,eAAc,IAAI,MAAM;AAChC,YAAMA,eAAc,UAAU,WAAW;AACzC,UAAI,KAAK,EAAE,SAAS,MAAM,MAAM,KAAK,CAAC;AAAA,IACxC,SAAS,OAAO;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,IAAI,cAAc,OAAO,KAAK,QAAQ;AACxC,QAAI;AACF,YAAM,EAAE,WAAW,QAAQ,GAAG,WAAW,IAAI,IAAI;AACjD,YAAM,cAAc,MAAM,QAAQ,YAAY,SAAS;AACvD,UAAI,CAAC,aAAa;AAChB,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;AAAA,MACF;AACA,YAAM,YAAY,YAAY,MAAM,UAAU,CAAC,MAAW,EAAE,OAAO,MAAM;AACzE,UAAI,cAAc,IAAI;AACpB,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAChD;AAAA,MACF;AACA,YAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,YAAM,eAAe,YAAY,MAAM,SAAS;AAChD,YAAM,cAAc;AAAA,QAClB,GAAG;AAAA,QACH,GAAG;AAAA,QACH,IAAI,aAAa;AAAA,QACjB,WAAW,aAAa;AAAA,QACxB,WAAW,aAAa;AAAA,QACxB,WAAW;AAAA,QACX,aAAa,WAAW,WAAW,UAAU,aAAa,WAAW,SACjE,MACA,WAAW,UAAU,WAAW,WAAW,SACzC,OACA,aAAa;AAAA,MACrB;AACA,kBAAY,MAAM,SAAS,IAAI;AAC/B,kBAAY,QAAQ,YAAY;AAChC,YAAM,WAAW,QAAQ,YAAY,SAAS;AAC9C,YAAM,EAAE,eAAAA,eAAc,IAAI,MAAM;AAChC,YAAMA,eAAc,UAAU,WAAW;AACzC,UAAI,KAAK,EAAE,SAAS,MAAM,MAAM,YAAY,CAAC;AAAA,IAC/C,SAAS,OAAO;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,OAAO,cAAc,OAAO,KAAK,QAAQ;AAC3C,QAAI;AACF,YAAM,EAAE,WAAW,OAAO,IAAI,IAAI;AAClC,YAAM,cAAc,MAAM,QAAQ,YAAY,SAAmB;AACjE,UAAI,CAAC,aAAa;AAChB,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;AAAA,MACF;AACA,YAAM,YAAY,YAAY,MAAM,UAAU,CAAC,MAAW,EAAE,OAAO,MAAM;AACzE,UAAI,cAAc,IAAI;AACpB,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAChD;AAAA,MACF;AACA,kBAAY,MAAM,OAAO,WAAW,CAAC;AACrC,kBAAY,QAAQ,aAAY,oBAAI,KAAK,GAAE,YAAY;AACvD,YAAM,WAAW,QAAQ,YAAY,SAAmB;AACxD,YAAM,EAAE,eAAAA,eAAc,IAAI,MAAM;AAChC,YAAMA,eAAc,UAAU,WAAW;AACzC,UAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,IAC5B,SAAS,OAAO;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,QAAM,WAAgB,WAAK,QAAQ,IAAI,GAAG,QAAQ,OAAO,KAAK;AAC9D,MAAI,IAAI,QAAQ,OAAO,QAAQ,CAAC;AAEhC,MAAI,IAAI,KAAK,CAAC,KAAK,QAAQ;AACzB,QAAI,IAAI,KAAK,WAAW,MAAM,GAAG;AAC/B,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC/C;AAAA,IACF;AACA,QAAI,SAAc,WAAK,UAAU,YAAY,CAAC;AAAA,EAChD,CAAC;AAED,QAAM,SAAS,IAAI,OAAO,MAAM,WAAW,MAAM;AAC/C,YAAQ,IAAI,kDAAkD,IAAI,EAAE;AAAA,EACtE,CAAC;AAED,SAAO;AACT;","names":["path","path","path","fs","writeJsonFile"]}
|
|
1
|
+
{"version":3,"sources":["../../node_modules/tsup/assets/esm_shims.js","../../src/utils/file-helpers.ts","../../src/web/server.ts","../../src/storage/index.ts","../../src/utils/path-helpers.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import * as fs from 'fs/promises';\n\n/**\n * Read and parse a JSON file\n * @param filePath - Path to the JSON file\n * @returns Parsed JSON data\n * @throws Error if file cannot be read or parsed\n */\nexport async function readJsonFile<T>(filePath: string): Promise<T> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to read JSON file ${filePath}: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Write data to a JSON file\n * @param filePath - Path to the JSON file\n * @param data - Data to serialize and write\n * @throws Error if file cannot be written\n */\nexport async function writeJsonFile<T>(filePath: string, data: T): Promise<void> {\n try {\n const content = JSON.stringify(data, null, 2);\n await fs.writeFile(filePath, content, 'utf-8');\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to write JSON file ${filePath}: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Ensure a directory exists, creating it recursively if needed\n * @param dirPath - Path to the directory\n * @throws Error if directory cannot be created\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n try {\n await fs.mkdir(dirPath, { recursive: true });\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);\n }\n throw error;\n }\n}\n","import express from 'express';\nimport * as path from 'path';\nimport type { Server } from 'http';\nimport { storage } from '../storage/index.js';\n\nexport function createServer(port: number = 7860): Promise<Server> {\n return new Promise((resolve, reject) => {\n const app = express();\n\n app.use(express.json());\n\n app.get('/api/projects', async (_req, res) => {\n try {\n const projects = await storage.listProjects();\n res.json(projects);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/projects/:id', async (req, res) => {\n try {\n const project = await storage.readProject(req.params.id);\n if (!project) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n res.json(project);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/tasks', async (req, res) => {\n try {\n const filters: Record<string, unknown> = { ...req.query };\n\n // Convert includeCompleted from string to boolean\n if (filters.includeCompleted !== undefined) {\n filters.includeCompleted = filters.includeCompleted === 'true';\n }\n\n const tasks = await storage.searchTasks(filters as any);\n res.json(tasks);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/projects', async (req, res) => {\n try {\n const project = await storage.createProject(req.body);\n res.json({ success: true, data: project });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/projects', async (req, res) => {\n try {\n const { projectId, ...updateData } = req.body;\n const project = await storage.updateProject(projectId, updateData);\n res.json({ success: true, data: project });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/projects', async (req, res) => {\n try {\n const { projectId } = req.query;\n await storage.deleteProject(projectId as string);\n res.json({ success: true });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/tasks', async (req, res) => {\n try {\n const { projectId, ...taskData } = req.body;\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n const now = new Date().toISOString();\n const task = {\n id: `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,\n projectId,\n ...taskData,\n status: taskData.status || 'todo',\n priority: taskData.priority || 'medium',\n tags: taskData.tags || [],\n dueDate: taskData.dueDate || null,\n assignee: taskData.assignee || null,\n createdAt: now,\n updatedAt: now,\n completedAt: null,\n };\n projectData.tasks.push(task);\n projectData.project.updatedAt = now;\n const filePath = storage.getFilePath(projectId);\n const { writeJsonFile } = await import('../utils/file-helpers.js');\n await writeJsonFile(filePath, projectData);\n res.json({ success: true, data: task });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/tasks', async (req, res) => {\n try {\n const { projectId, taskId, ...updateData } = req.body;\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n const taskIndex = projectData.tasks.findIndex((t: any) => t.id === taskId);\n if (taskIndex === -1) {\n res.status(404).json({ error: 'Task not found' });\n return;\n }\n const now = new Date().toISOString();\n const existingTask = projectData.tasks[taskIndex];\n const updatedTask = {\n ...existingTask,\n ...updateData,\n id: existingTask.id,\n projectId: existingTask.projectId,\n createdAt: existingTask.createdAt,\n updatedAt: now,\n completedAt: updateData.status === 'done' && existingTask.status !== 'done'\n ? now\n : updateData.status && updateData.status !== 'done'\n ? null\n : existingTask.completedAt,\n };\n projectData.tasks[taskIndex] = updatedTask;\n projectData.project.updatedAt = now;\n const filePath = storage.getFilePath(projectId);\n const { writeJsonFile } = await import('../utils/file-helpers.js');\n await writeJsonFile(filePath, projectData);\n res.json({ success: true, data: updatedTask });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/tasks', async (req, res) => {\n try {\n const { projectId, taskId } = req.query;\n const projectData = await storage.readProject(projectId as string);\n if (!projectData) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n const taskIndex = projectData.tasks.findIndex((t: any) => t.id === taskId);\n if (taskIndex === -1) {\n res.status(404).json({ error: 'Task not found' });\n return;\n }\n projectData.tasks.splice(taskIndex, 1);\n projectData.project.updatedAt = new Date().toISOString();\n const filePath = storage.getFilePath(projectId as string);\n const { writeJsonFile } = await import('../utils/file-helpers.js');\n await writeJsonFile(filePath, projectData);\n res.json({ success: true });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n const distPath = path.join(process.cwd(), 'dist', 'web', 'app');\n app.use(express.static(distPath));\n\n app.get('*', (req, res) => {\n if (req.path.startsWith('/api')) {\n res.status(404).json({ error: 'API not found' });\n return;\n }\n res.sendFile(path.join(distPath, 'index.html'));\n });\n\n const server = app.listen(port, '127.0.0.1');\n\n server.once('listening', () => {\n console.log(`Web interface server running at http://localhost:${port}`);\n resolve(server);\n });\n\n server.once('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EADDRINUSE') {\n reject(new Error(`Port ${port} is already in use`));\n return;\n }\n\n reject(error);\n });\n });\n}\n","import * as path from 'path';\nimport type {\n Project,\n ProjectData,\n Task,\n TaskSearchFilters,\n CreateProjectInput,\n UpdateProjectInput,\n} from '../models/index.js';\nimport { getStorageDir } from '../utils/path-helpers.js';\nimport { readJsonFile, writeJsonFile, ensureDir } from '../utils/file-helpers.js';\n\n/**\n * Storage class for managing roadmap-skill projects\n * Projects are stored as individual JSON files in ~/.roadmap-skill/projects/\n */\nexport class ProjectStorage {\n private storageDir: string;\n\n constructor() {\n this.storageDir = getStorageDir();\n }\n\n /**\n * Ensure the storage directory exists\n */\n async ensureDirectory(): Promise<void> {\n await ensureDir(this.storageDir);\n }\n\n /**\n * Get the file path for a project\n * @param projectId - The project ID\n * @returns Full path to the project JSON file\n */\n getFilePath(projectId: string): string {\n return path.join(this.storageDir, `${projectId}.json`);\n }\n\n /**\n * Create a new project\n * @param input - Project creation data\n * @returns The created project data\n */\n async createProject(input: CreateProjectInput): Promise<ProjectData> {\n await this.ensureDirectory();\n\n const now = new Date().toISOString();\n const projectId = `proj_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n const project: Project = {\n id: projectId,\n name: input.name,\n description: input.description,\n projectType: input.projectType,\n status: 'active',\n startDate: input.startDate,\n targetDate: input.targetDate,\n createdAt: now,\n updatedAt: now,\n };\n\n const projectData: ProjectData = {\n version: 1,\n project,\n milestones: [],\n tasks: [],\n tags: [],\n };\n\n const filePath = this.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return projectData;\n }\n\n /**\n * Read a project by ID\n * @param projectId - The project ID\n * @returns The project data or null if not found\n */\n async readProject(projectId: string): Promise<ProjectData | null> {\n try {\n const filePath = this.getFilePath(projectId);\n return await readJsonFile<ProjectData>(filePath);\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Update an existing project\n * @param projectId - The project ID\n * @param input - Project update data\n * @returns The updated project data or null if not found\n */\n async updateProject(\n projectId: string,\n input: UpdateProjectInput\n ): Promise<ProjectData | null> {\n const projectData = await this.readProject(projectId);\n if (!projectData) {\n return null;\n }\n\n const now = new Date().toISOString();\n\n projectData.project = {\n ...projectData.project,\n ...input,\n updatedAt: now,\n };\n\n const filePath = this.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return projectData;\n }\n\n /**\n * Delete a project by ID\n * @param projectId - The project ID\n * @returns True if deleted, false if not found\n */\n async deleteProject(projectId: string): Promise<boolean> {\n try {\n const filePath = this.getFilePath(projectId);\n const fs = await import('fs/promises');\n await fs.unlink(filePath);\n return true;\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * List all projects sorted by updatedAt (descending)\n * @returns Array of project summaries (project + metadata)\n */\n async listProjects(): Promise<Array<{ project: Project; taskCount: number; milestoneCount: number }>> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const projects: Array<{ project: Project; taskCount: number; milestoneCount: number }> = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n projects.push({\n project: data.project,\n taskCount: data.tasks.length,\n milestoneCount: data.milestones.length,\n });\n } catch {\n // Skip invalid files\n continue;\n }\n }\n\n // Sort by updatedAt descending\n return projects.sort(\n (a, b) => new Date(b.project.updatedAt).getTime() - new Date(a.project.updatedAt).getTime()\n );\n }\n\n /**\n * Search tasks across all projects with filters\n * @param filters - Search filters\n * @returns Array of matching tasks with project context\n */\n async searchTasks(\n filters: TaskSearchFilters\n ): Promise<Array<{ task: Task; project: Project }>> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const results: Array<{ task: Task; project: Project }> = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n\n // Skip if project filter doesn't match\n if (filters.projectId && data.project.id !== filters.projectId) {\n continue;\n }\n\n for (const task of data.tasks) {\n // Apply filters\n if (filters.status && task.status !== filters.status) {\n continue;\n }\n if (filters.priority && task.priority !== filters.priority) {\n continue;\n }\n if (filters.assignee && task.assignee !== filters.assignee) {\n continue;\n }\n if (filters.dueBefore && task.dueDate && task.dueDate > filters.dueBefore) {\n continue;\n }\n if (filters.dueAfter && task.dueDate && task.dueDate < filters.dueAfter) {\n continue;\n }\n if (\n filters.tags &&\n filters.tags.length > 0 &&\n !filters.tags.some((tag) => task.tags.includes(tag))\n ) {\n continue;\n }\n if (\n filters.searchText &&\n !task.title.toLowerCase().includes(filters.searchText.toLowerCase()) &&\n !task.description.toLowerCase().includes(filters.searchText.toLowerCase())\n ) {\n continue;\n }\n\n // Filter completed tasks (done status) when includeCompleted is false\n if (filters.includeCompleted === false && task.status === 'done') {\n continue;\n }\n\n results.push({ task, project: data.project });\n }\n } catch {\n // Skip invalid files\n continue;\n }\n }\n\n return results;\n }\n}\n\n// Export singleton instance\nexport const storage = new ProjectStorage();\n","import * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Get the storage directory for roadmap-skill projects\n * Returns: ~/.roadmap-skill/projects\n */\nexport function getStorageDir(): string {\n const homeDir = os.homedir();\n return path.join(homeDir, '.roadmap-skill', 'projects');\n}\n"],"mappings":"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","names":["path","path","path","fs","writeJsonFile"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roadmap-skill",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "A MCP server for project roadmap management with task tracking, tagging, and web visualization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"vitest": "^1.1.0"
|
|
59
59
|
},
|
|
60
60
|
"engines": {
|
|
61
|
-
"node": ">=
|
|
61
|
+
"node": ">=20.0.0"
|
|
62
62
|
},
|
|
63
63
|
"bin": {
|
|
64
64
|
"roadmap-skill": "./dist/index.js",
|
|
@@ -71,4 +71,4 @@
|
|
|
71
71
|
"LICENSE",
|
|
72
72
|
"CHANGELOG.md"
|
|
73
73
|
]
|
|
74
|
-
}
|
|
74
|
+
}
|