roadmap-skill 0.1.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.
@@ -0,0 +1,441 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // node_modules/tsup/assets/esm_shims.js
12
+ import path from "path";
13
+ import { fileURLToPath } from "url";
14
+ var init_esm_shims = __esm({
15
+ "node_modules/tsup/assets/esm_shims.js"() {
16
+ "use strict";
17
+ }
18
+ });
19
+
20
+ // src/utils/file-helpers.ts
21
+ var file_helpers_exports = {};
22
+ __export(file_helpers_exports, {
23
+ ensureDir: () => ensureDir,
24
+ readJsonFile: () => readJsonFile,
25
+ writeJsonFile: () => writeJsonFile
26
+ });
27
+ import * as fs from "fs/promises";
28
+ async function readJsonFile(filePath) {
29
+ try {
30
+ const content = await fs.readFile(filePath, "utf-8");
31
+ return JSON.parse(content);
32
+ } catch (error) {
33
+ if (error instanceof Error) {
34
+ throw new Error(`Failed to read JSON file ${filePath}: ${error.message}`);
35
+ }
36
+ throw error;
37
+ }
38
+ }
39
+ async function writeJsonFile(filePath, data) {
40
+ try {
41
+ const content = JSON.stringify(data, null, 2);
42
+ await fs.writeFile(filePath, content, "utf-8");
43
+ } catch (error) {
44
+ if (error instanceof Error) {
45
+ throw new Error(`Failed to write JSON file ${filePath}: ${error.message}`);
46
+ }
47
+ throw error;
48
+ }
49
+ }
50
+ async function ensureDir(dirPath) {
51
+ try {
52
+ await fs.mkdir(dirPath, { recursive: true });
53
+ } catch (error) {
54
+ if (error instanceof Error) {
55
+ throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
56
+ }
57
+ throw error;
58
+ }
59
+ }
60
+ var init_file_helpers = __esm({
61
+ "src/utils/file-helpers.ts"() {
62
+ "use strict";
63
+ init_esm_shims();
64
+ }
65
+ });
66
+
67
+ // src/web/server.ts
68
+ init_esm_shims();
69
+ import express from "express";
70
+ import * as path4 from "path";
71
+ import { fileURLToPath as fileURLToPath2 } from "url";
72
+
73
+ // src/storage/index.ts
74
+ init_esm_shims();
75
+ import * as path3 from "path";
76
+
77
+ // src/utils/path-helpers.ts
78
+ init_esm_shims();
79
+ import * as os from "os";
80
+ import * as path2 from "path";
81
+ function getStorageDir() {
82
+ const homeDir = os.homedir();
83
+ return path2.join(homeDir, ".roadmap-skill", "projects");
84
+ }
85
+
86
+ // src/storage/index.ts
87
+ init_file_helpers();
88
+ var ProjectStorage = class {
89
+ storageDir;
90
+ constructor() {
91
+ this.storageDir = getStorageDir();
92
+ }
93
+ /**
94
+ * Ensure the storage directory exists
95
+ */
96
+ async ensureDirectory() {
97
+ await ensureDir(this.storageDir);
98
+ }
99
+ /**
100
+ * Get the file path for a project
101
+ * @param projectId - The project ID
102
+ * @returns Full path to the project JSON file
103
+ */
104
+ getFilePath(projectId) {
105
+ return path3.join(this.storageDir, `${projectId}.json`);
106
+ }
107
+ /**
108
+ * Create a new project
109
+ * @param input - Project creation data
110
+ * @returns The created project data
111
+ */
112
+ async createProject(input) {
113
+ await this.ensureDirectory();
114
+ const now = (/* @__PURE__ */ new Date()).toISOString();
115
+ const projectId = `proj_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
116
+ const project = {
117
+ id: projectId,
118
+ name: input.name,
119
+ description: input.description,
120
+ projectType: input.projectType,
121
+ status: "active",
122
+ startDate: input.startDate,
123
+ targetDate: input.targetDate,
124
+ createdAt: now,
125
+ updatedAt: now
126
+ };
127
+ const projectData = {
128
+ version: 1,
129
+ project,
130
+ milestones: [],
131
+ tasks: [],
132
+ tags: []
133
+ };
134
+ const filePath = this.getFilePath(projectId);
135
+ await writeJsonFile(filePath, projectData);
136
+ return projectData;
137
+ }
138
+ /**
139
+ * Read a project by ID
140
+ * @param projectId - The project ID
141
+ * @returns The project data or null if not found
142
+ */
143
+ async readProject(projectId) {
144
+ try {
145
+ const filePath = this.getFilePath(projectId);
146
+ return await readJsonFile(filePath);
147
+ } catch (error) {
148
+ if (error instanceof Error && error.message.includes("ENOENT")) {
149
+ return null;
150
+ }
151
+ throw error;
152
+ }
153
+ }
154
+ /**
155
+ * Update an existing project
156
+ * @param projectId - The project ID
157
+ * @param input - Project update data
158
+ * @returns The updated project data or null if not found
159
+ */
160
+ async updateProject(projectId, input) {
161
+ const projectData = await this.readProject(projectId);
162
+ if (!projectData) {
163
+ return null;
164
+ }
165
+ const now = (/* @__PURE__ */ new Date()).toISOString();
166
+ projectData.project = {
167
+ ...projectData.project,
168
+ ...input,
169
+ updatedAt: now
170
+ };
171
+ const filePath = this.getFilePath(projectId);
172
+ await writeJsonFile(filePath, projectData);
173
+ return projectData;
174
+ }
175
+ /**
176
+ * Delete a project by ID
177
+ * @param projectId - The project ID
178
+ * @returns True if deleted, false if not found
179
+ */
180
+ async deleteProject(projectId) {
181
+ try {
182
+ const filePath = this.getFilePath(projectId);
183
+ const fs2 = await import("fs/promises");
184
+ await fs2.unlink(filePath);
185
+ return true;
186
+ } catch (error) {
187
+ if (error instanceof Error && error.message.includes("ENOENT")) {
188
+ return false;
189
+ }
190
+ throw error;
191
+ }
192
+ }
193
+ /**
194
+ * List all projects sorted by updatedAt (descending)
195
+ * @returns Array of project summaries (project + metadata)
196
+ */
197
+ async listProjects() {
198
+ await this.ensureDirectory();
199
+ const fs2 = await import("fs/promises");
200
+ const files = await fs2.readdir(this.storageDir);
201
+ const jsonFiles = files.filter((f) => f.endsWith(".json"));
202
+ const projects = [];
203
+ for (const file of jsonFiles) {
204
+ try {
205
+ const filePath = path3.join(this.storageDir, file);
206
+ const data = await readJsonFile(filePath);
207
+ projects.push({
208
+ project: data.project,
209
+ taskCount: data.tasks.length,
210
+ milestoneCount: data.milestones.length
211
+ });
212
+ } catch {
213
+ continue;
214
+ }
215
+ }
216
+ return projects.sort(
217
+ (a, b) => new Date(b.project.updatedAt).getTime() - new Date(a.project.updatedAt).getTime()
218
+ );
219
+ }
220
+ /**
221
+ * Search tasks across all projects with filters
222
+ * @param filters - Search filters
223
+ * @returns Array of matching tasks with project context
224
+ */
225
+ async searchTasks(filters) {
226
+ await this.ensureDirectory();
227
+ const fs2 = await import("fs/promises");
228
+ const files = await fs2.readdir(this.storageDir);
229
+ const jsonFiles = files.filter((f) => f.endsWith(".json"));
230
+ const results = [];
231
+ for (const file of jsonFiles) {
232
+ try {
233
+ const filePath = path3.join(this.storageDir, file);
234
+ const data = await readJsonFile(filePath);
235
+ if (filters.projectId && data.project.id !== filters.projectId) {
236
+ continue;
237
+ }
238
+ for (const task of data.tasks) {
239
+ if (filters.status && task.status !== filters.status) {
240
+ continue;
241
+ }
242
+ if (filters.priority && task.priority !== filters.priority) {
243
+ continue;
244
+ }
245
+ if (filters.assignee && task.assignee !== filters.assignee) {
246
+ continue;
247
+ }
248
+ if (filters.dueBefore && task.dueDate && task.dueDate > filters.dueBefore) {
249
+ continue;
250
+ }
251
+ if (filters.dueAfter && task.dueDate && task.dueDate < filters.dueAfter) {
252
+ continue;
253
+ }
254
+ if (filters.tags && filters.tags.length > 0 && !filters.tags.some((tag) => task.tags.includes(tag))) {
255
+ continue;
256
+ }
257
+ if (filters.searchText && !task.title.toLowerCase().includes(filters.searchText.toLowerCase()) && !task.description.toLowerCase().includes(filters.searchText.toLowerCase())) {
258
+ continue;
259
+ }
260
+ results.push({ task, project: data.project });
261
+ }
262
+ } catch {
263
+ continue;
264
+ }
265
+ }
266
+ return results;
267
+ }
268
+ };
269
+ var storage = new ProjectStorage();
270
+
271
+ // src/web/server.ts
272
+ var __filename2 = fileURLToPath2(import.meta.url);
273
+ var __dirname2 = path4.dirname(__filename2);
274
+ function createServer(port = 7860) {
275
+ const app = express();
276
+ app.use(express.json());
277
+ app.get("/api/projects", async (_req, res) => {
278
+ try {
279
+ const projects = await storage.listProjects();
280
+ res.json(projects);
281
+ } catch (error) {
282
+ res.status(500).json({ error: error.message });
283
+ }
284
+ });
285
+ app.get("/api/projects/:id", async (req, res) => {
286
+ try {
287
+ const project = await storage.readProject(req.params.id);
288
+ if (!project) {
289
+ res.status(404).json({ error: "Project not found" });
290
+ return;
291
+ }
292
+ res.json(project);
293
+ } catch (error) {
294
+ res.status(500).json({ error: error.message });
295
+ }
296
+ });
297
+ app.get("/api/tasks", async (req, res) => {
298
+ try {
299
+ const filters = req.query;
300
+ const tasks = await storage.searchTasks(filters);
301
+ res.json(tasks);
302
+ } catch (error) {
303
+ res.status(500).json({ error: error.message });
304
+ }
305
+ });
306
+ app.post("/api/projects", async (req, res) => {
307
+ try {
308
+ const project = await storage.createProject(req.body);
309
+ res.json({ success: true, data: project });
310
+ } catch (error) {
311
+ res.status(500).json({ error: error.message });
312
+ }
313
+ });
314
+ app.put("/api/projects", async (req, res) => {
315
+ try {
316
+ const { projectId, ...updateData } = req.body;
317
+ const project = await storage.updateProject(projectId, updateData);
318
+ res.json({ success: true, data: project });
319
+ } catch (error) {
320
+ res.status(500).json({ error: error.message });
321
+ }
322
+ });
323
+ app.delete("/api/projects", async (req, res) => {
324
+ try {
325
+ const { projectId } = req.query;
326
+ await storage.deleteProject(projectId);
327
+ res.json({ success: true });
328
+ } catch (error) {
329
+ res.status(500).json({ error: error.message });
330
+ }
331
+ });
332
+ app.post("/api/tasks", async (req, res) => {
333
+ try {
334
+ const { projectId, ...taskData } = req.body;
335
+ const projectData = await storage.readProject(projectId);
336
+ if (!projectData) {
337
+ res.status(404).json({ error: "Project not found" });
338
+ return;
339
+ }
340
+ const now = (/* @__PURE__ */ new Date()).toISOString();
341
+ const task = {
342
+ id: `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
343
+ projectId,
344
+ ...taskData,
345
+ status: taskData.status || "todo",
346
+ priority: taskData.priority || "medium",
347
+ tags: taskData.tags || [],
348
+ dueDate: taskData.dueDate || null,
349
+ assignee: taskData.assignee || null,
350
+ createdAt: now,
351
+ updatedAt: now,
352
+ completedAt: null
353
+ };
354
+ projectData.tasks.push(task);
355
+ projectData.project.updatedAt = now;
356
+ const filePath = storage.getFilePath(projectId);
357
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
358
+ await writeJsonFile2(filePath, projectData);
359
+ res.json({ success: true, data: task });
360
+ } catch (error) {
361
+ res.status(500).json({ error: error.message });
362
+ }
363
+ });
364
+ app.put("/api/tasks", async (req, res) => {
365
+ try {
366
+ const { projectId, taskId, ...updateData } = req.body;
367
+ const projectData = await storage.readProject(projectId);
368
+ if (!projectData) {
369
+ res.status(404).json({ error: "Project not found" });
370
+ return;
371
+ }
372
+ const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);
373
+ if (taskIndex === -1) {
374
+ res.status(404).json({ error: "Task not found" });
375
+ return;
376
+ }
377
+ const now = (/* @__PURE__ */ new Date()).toISOString();
378
+ const existingTask = projectData.tasks[taskIndex];
379
+ const updatedTask = {
380
+ ...existingTask,
381
+ ...updateData,
382
+ id: existingTask.id,
383
+ projectId: existingTask.projectId,
384
+ createdAt: existingTask.createdAt,
385
+ updatedAt: now,
386
+ completedAt: updateData.status === "done" && existingTask.status !== "done" ? now : updateData.status && updateData.status !== "done" ? null : existingTask.completedAt
387
+ };
388
+ projectData.tasks[taskIndex] = updatedTask;
389
+ projectData.project.updatedAt = now;
390
+ const filePath = storage.getFilePath(projectId);
391
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
392
+ await writeJsonFile2(filePath, projectData);
393
+ res.json({ success: true, data: updatedTask });
394
+ } catch (error) {
395
+ res.status(500).json({ error: error.message });
396
+ }
397
+ });
398
+ app.delete("/api/tasks", async (req, res) => {
399
+ try {
400
+ const { projectId, taskId } = req.query;
401
+ const projectData = await storage.readProject(projectId);
402
+ if (!projectData) {
403
+ res.status(404).json({ error: "Project not found" });
404
+ return;
405
+ }
406
+ const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);
407
+ if (taskIndex === -1) {
408
+ res.status(404).json({ error: "Task not found" });
409
+ return;
410
+ }
411
+ projectData.tasks.splice(taskIndex, 1);
412
+ projectData.project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
413
+ const filePath = storage.getFilePath(projectId);
414
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
415
+ await writeJsonFile2(filePath, projectData);
416
+ res.json({ success: true });
417
+ } catch (error) {
418
+ res.status(500).json({ error: error.message });
419
+ }
420
+ });
421
+ const distPath = path4.join(__dirname2, "app");
422
+ app.use(express.static(distPath));
423
+ app.get("*", (req, res) => {
424
+ if (req.path.startsWith("/api")) {
425
+ res.status(404).json({ error: "API not found" });
426
+ return;
427
+ }
428
+ res.sendFile(path4.join(distPath, "index.html"));
429
+ });
430
+ const server = app.listen(port, "0.0.0.0", () => {
431
+ console.log(`Web interface server running at http://0.0.0.0:${port}`);
432
+ });
433
+ return server;
434
+ }
435
+ export {
436
+ createServer
437
+ };
438
+ //# sourceMappingURL=server.js.map
439
+ const port = process.env.PORT ? parseInt(process.env.PORT) : 7860;
440
+ createServer(port);
441
+
@@ -0,0 +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 { fileURLToPath } from 'url';\nimport { storage } from '../storage/index.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\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(__dirname, '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":"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","names":["path","fileURLToPath","path","path","fs","__filename","fileURLToPath","__dirname","writeJsonFile"]}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "roadmap-skill",
3
+ "version": "0.1.0",
4
+ "description": "A MCP server for project roadmap management with task tracking, tagging, and web visualization",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "npm run build:web && tsup",
10
+ "build:web": "vite build",
11
+ "dev": "tsup --watch",
12
+ "test": "vitest",
13
+ "test:unit": "vitest run tests/unit",
14
+ "test:integration": "vitest run tests/integration",
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "roadmap",
20
+ "project-management",
21
+ "ai",
22
+ "claude",
23
+ "cursor",
24
+ "vscode",
25
+ "task-management",
26
+ "kanban"
27
+ ],
28
+ "author": "Your Name",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/yourusername/roadmap-skill.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/yourusername/roadmap-skill/issues"
36
+ },
37
+ "homepage": "https://github.com/yourusername/roadmap-skill#readme",
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.0.4",
40
+ "express": "^4.18.2",
41
+ "react": "^19.2.4",
42
+ "react-dom": "^19.2.4",
43
+ "uuid": "^9.0.1",
44
+ "zod": "^3.22.4"
45
+ },
46
+ "devDependencies": {
47
+ "@types/express": "^4.17.21",
48
+ "@types/node": "^20.19.33",
49
+ "@types/react": "^19.2.13",
50
+ "@types/react-dom": "^19.2.3",
51
+ "@types/uuid": "^9.0.7",
52
+ "@vitejs/plugin-react": "^5.1.3",
53
+ "tsup": "^8.0.1",
54
+ "typescript": "^5.3.3",
55
+ "vite": "^7.3.1",
56
+ "vitest": "^1.1.0"
57
+ },
58
+ "engines": {
59
+ "node": ">=18.0.0"
60
+ },
61
+ "bin": {
62
+ "roadmap-skill": "./dist/index.js",
63
+ "roadmap-skill-web": "./dist/web/server.js"
64
+ },
65
+ "files": [
66
+ "dist/",
67
+ "templates/",
68
+ "README.md",
69
+ "LICENSE",
70
+ "CHANGELOG.md"
71
+ ]
72
+ }