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.
package/dist/index.js ADDED
@@ -0,0 +1,2203 @@
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/index.ts
68
+ init_esm_shims();
69
+
70
+ // src/server.ts
71
+ init_esm_shims();
72
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
73
+ import {
74
+ CallToolRequestSchema,
75
+ ListToolsRequestSchema,
76
+ ListResourcesRequestSchema,
77
+ ReadResourceRequestSchema,
78
+ ListPromptsRequestSchema,
79
+ GetPromptRequestSchema
80
+ } from "@modelcontextprotocol/sdk/types.js";
81
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
82
+
83
+ // src/tools/index.ts
84
+ init_esm_shims();
85
+
86
+ // src/tools/project-tools.ts
87
+ init_esm_shims();
88
+ import { z } from "zod";
89
+
90
+ // src/storage/index.ts
91
+ init_esm_shims();
92
+ import * as path3 from "path";
93
+
94
+ // src/utils/path-helpers.ts
95
+ init_esm_shims();
96
+ import * as os from "os";
97
+ import * as path2 from "path";
98
+ function getStorageDir() {
99
+ const homeDir = os.homedir();
100
+ return path2.join(homeDir, ".roadmap-skill", "projects");
101
+ }
102
+
103
+ // src/storage/index.ts
104
+ init_file_helpers();
105
+ var ProjectStorage = class {
106
+ storageDir;
107
+ constructor() {
108
+ this.storageDir = getStorageDir();
109
+ }
110
+ /**
111
+ * Ensure the storage directory exists
112
+ */
113
+ async ensureDirectory() {
114
+ await ensureDir(this.storageDir);
115
+ }
116
+ /**
117
+ * Get the file path for a project
118
+ * @param projectId - The project ID
119
+ * @returns Full path to the project JSON file
120
+ */
121
+ getFilePath(projectId) {
122
+ return path3.join(this.storageDir, `${projectId}.json`);
123
+ }
124
+ /**
125
+ * Create a new project
126
+ * @param input - Project creation data
127
+ * @returns The created project data
128
+ */
129
+ async createProject(input) {
130
+ await this.ensureDirectory();
131
+ const now = (/* @__PURE__ */ new Date()).toISOString();
132
+ const projectId = `proj_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
133
+ const project = {
134
+ id: projectId,
135
+ name: input.name,
136
+ description: input.description,
137
+ projectType: input.projectType,
138
+ status: "active",
139
+ startDate: input.startDate,
140
+ targetDate: input.targetDate,
141
+ createdAt: now,
142
+ updatedAt: now
143
+ };
144
+ const projectData = {
145
+ version: 1,
146
+ project,
147
+ milestones: [],
148
+ tasks: [],
149
+ tags: []
150
+ };
151
+ const filePath = this.getFilePath(projectId);
152
+ await writeJsonFile(filePath, projectData);
153
+ return projectData;
154
+ }
155
+ /**
156
+ * Read a project by ID
157
+ * @param projectId - The project ID
158
+ * @returns The project data or null if not found
159
+ */
160
+ async readProject(projectId) {
161
+ try {
162
+ const filePath = this.getFilePath(projectId);
163
+ return await readJsonFile(filePath);
164
+ } catch (error) {
165
+ if (error instanceof Error && error.message.includes("ENOENT")) {
166
+ return null;
167
+ }
168
+ throw error;
169
+ }
170
+ }
171
+ /**
172
+ * Update an existing project
173
+ * @param projectId - The project ID
174
+ * @param input - Project update data
175
+ * @returns The updated project data or null if not found
176
+ */
177
+ async updateProject(projectId, input) {
178
+ const projectData = await this.readProject(projectId);
179
+ if (!projectData) {
180
+ return null;
181
+ }
182
+ const now = (/* @__PURE__ */ new Date()).toISOString();
183
+ projectData.project = {
184
+ ...projectData.project,
185
+ ...input,
186
+ updatedAt: now
187
+ };
188
+ const filePath = this.getFilePath(projectId);
189
+ await writeJsonFile(filePath, projectData);
190
+ return projectData;
191
+ }
192
+ /**
193
+ * Delete a project by ID
194
+ * @param projectId - The project ID
195
+ * @returns True if deleted, false if not found
196
+ */
197
+ async deleteProject(projectId) {
198
+ try {
199
+ const filePath = this.getFilePath(projectId);
200
+ const fs3 = await import("fs/promises");
201
+ await fs3.unlink(filePath);
202
+ return true;
203
+ } catch (error) {
204
+ if (error instanceof Error && error.message.includes("ENOENT")) {
205
+ return false;
206
+ }
207
+ throw error;
208
+ }
209
+ }
210
+ /**
211
+ * List all projects sorted by updatedAt (descending)
212
+ * @returns Array of project summaries (project + metadata)
213
+ */
214
+ async listProjects() {
215
+ await this.ensureDirectory();
216
+ const fs3 = await import("fs/promises");
217
+ const files = await fs3.readdir(this.storageDir);
218
+ const jsonFiles = files.filter((f) => f.endsWith(".json"));
219
+ const projects = [];
220
+ for (const file of jsonFiles) {
221
+ try {
222
+ const filePath = path3.join(this.storageDir, file);
223
+ const data = await readJsonFile(filePath);
224
+ projects.push({
225
+ project: data.project,
226
+ taskCount: data.tasks.length,
227
+ milestoneCount: data.milestones.length
228
+ });
229
+ } catch {
230
+ continue;
231
+ }
232
+ }
233
+ return projects.sort(
234
+ (a, b) => new Date(b.project.updatedAt).getTime() - new Date(a.project.updatedAt).getTime()
235
+ );
236
+ }
237
+ /**
238
+ * Search tasks across all projects with filters
239
+ * @param filters - Search filters
240
+ * @returns Array of matching tasks with project context
241
+ */
242
+ async searchTasks(filters) {
243
+ await this.ensureDirectory();
244
+ const fs3 = await import("fs/promises");
245
+ const files = await fs3.readdir(this.storageDir);
246
+ const jsonFiles = files.filter((f) => f.endsWith(".json"));
247
+ const results = [];
248
+ for (const file of jsonFiles) {
249
+ try {
250
+ const filePath = path3.join(this.storageDir, file);
251
+ const data = await readJsonFile(filePath);
252
+ if (filters.projectId && data.project.id !== filters.projectId) {
253
+ continue;
254
+ }
255
+ for (const task of data.tasks) {
256
+ if (filters.status && task.status !== filters.status) {
257
+ continue;
258
+ }
259
+ if (filters.priority && task.priority !== filters.priority) {
260
+ continue;
261
+ }
262
+ if (filters.assignee && task.assignee !== filters.assignee) {
263
+ continue;
264
+ }
265
+ if (filters.dueBefore && task.dueDate && task.dueDate > filters.dueBefore) {
266
+ continue;
267
+ }
268
+ if (filters.dueAfter && task.dueDate && task.dueDate < filters.dueAfter) {
269
+ continue;
270
+ }
271
+ if (filters.tags && filters.tags.length > 0 && !filters.tags.some((tag) => task.tags.includes(tag))) {
272
+ continue;
273
+ }
274
+ if (filters.searchText && !task.title.toLowerCase().includes(filters.searchText.toLowerCase()) && !task.description.toLowerCase().includes(filters.searchText.toLowerCase())) {
275
+ continue;
276
+ }
277
+ results.push({ task, project: data.project });
278
+ }
279
+ } catch {
280
+ continue;
281
+ }
282
+ }
283
+ return results;
284
+ }
285
+ };
286
+ var storage = new ProjectStorage();
287
+
288
+ // src/tools/project-tools.ts
289
+ var ProjectTypeEnum = z.enum(["roadmap", "skill-tree", "kanban"]);
290
+ var ProjectStatusEnum = z.enum(["active", "completed", "archived"]);
291
+ var createProjectTool = {
292
+ name: "create_project",
293
+ description: "Create a new project roadmap",
294
+ inputSchema: z.object({
295
+ name: z.string().min(1, "Project name is required"),
296
+ description: z.string(),
297
+ projectType: ProjectTypeEnum,
298
+ startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format"),
299
+ targetDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format")
300
+ }),
301
+ async execute(input) {
302
+ try {
303
+ const projectInput = {
304
+ name: input.name,
305
+ description: input.description,
306
+ projectType: input.projectType,
307
+ startDate: input.startDate,
308
+ targetDate: input.targetDate
309
+ };
310
+ const projectData = await storage.createProject(projectInput);
311
+ return {
312
+ success: true,
313
+ data: projectData
314
+ };
315
+ } catch (error) {
316
+ return {
317
+ success: false,
318
+ error: error instanceof Error ? error.message : "Failed to create project"
319
+ };
320
+ }
321
+ }
322
+ };
323
+ var listProjectsTool = {
324
+ name: "list_projects",
325
+ description: "List all projects with their task and milestone counts",
326
+ inputSchema: z.object({}),
327
+ async execute() {
328
+ try {
329
+ const projects = await storage.listProjects();
330
+ return {
331
+ success: true,
332
+ data: projects
333
+ };
334
+ } catch (error) {
335
+ return {
336
+ success: false,
337
+ error: error instanceof Error ? error.message : "Failed to list projects"
338
+ };
339
+ }
340
+ }
341
+ };
342
+ var getProjectTool = {
343
+ name: "get_project",
344
+ description: "Get a project by ID with all its data (tasks, tags, milestones)",
345
+ inputSchema: z.object({
346
+ projectId: z.string().min(1, "Project ID is required")
347
+ }),
348
+ async execute(input) {
349
+ try {
350
+ const projectData = await storage.readProject(input.projectId);
351
+ if (!projectData) {
352
+ return {
353
+ success: false,
354
+ error: `Project with ID '${input.projectId}' not found`
355
+ };
356
+ }
357
+ return {
358
+ success: true,
359
+ data: projectData
360
+ };
361
+ } catch (error) {
362
+ return {
363
+ success: false,
364
+ error: error instanceof Error ? error.message : "Failed to get project"
365
+ };
366
+ }
367
+ }
368
+ };
369
+ var updateProjectTool = {
370
+ name: "update_project",
371
+ description: "Update an existing project",
372
+ inputSchema: z.object({
373
+ projectId: z.string().min(1, "Project ID is required"),
374
+ name: z.string().min(1).optional(),
375
+ description: z.string().optional(),
376
+ projectType: ProjectTypeEnum.optional(),
377
+ status: ProjectStatusEnum.optional(),
378
+ startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
379
+ targetDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
380
+ }),
381
+ async execute(input) {
382
+ try {
383
+ const { projectId, ...updateData } = input;
384
+ if (Object.keys(updateData).length === 0) {
385
+ return {
386
+ success: false,
387
+ error: "At least one field to update is required"
388
+ };
389
+ }
390
+ const updateInput = updateData;
391
+ const projectData = await storage.updateProject(projectId, updateInput);
392
+ if (!projectData) {
393
+ return {
394
+ success: false,
395
+ error: `Project with ID '${projectId}' not found`
396
+ };
397
+ }
398
+ return {
399
+ success: true,
400
+ data: projectData
401
+ };
402
+ } catch (error) {
403
+ return {
404
+ success: false,
405
+ error: error instanceof Error ? error.message : "Failed to update project"
406
+ };
407
+ }
408
+ }
409
+ };
410
+ var deleteProjectTool = {
411
+ name: "delete_project",
412
+ description: "Delete a project by ID",
413
+ inputSchema: z.object({
414
+ projectId: z.string().min(1, "Project ID is required")
415
+ }),
416
+ async execute(input) {
417
+ try {
418
+ const deleted = await storage.deleteProject(input.projectId);
419
+ if (!deleted) {
420
+ return {
421
+ success: false,
422
+ error: `Project with ID '${input.projectId}' not found`
423
+ };
424
+ }
425
+ return {
426
+ success: true,
427
+ data: { deleted: true }
428
+ };
429
+ } catch (error) {
430
+ return {
431
+ success: false,
432
+ error: error instanceof Error ? error.message : "Failed to delete project"
433
+ };
434
+ }
435
+ }
436
+ };
437
+
438
+ // src/tools/task-tools.ts
439
+ init_esm_shims();
440
+ import { z as z2 } from "zod";
441
+ var TaskStatusEnum = z2.enum(["todo", "in-progress", "review", "done"]);
442
+ var TaskPriorityEnum = z2.enum(["low", "medium", "high", "critical"]);
443
+ function generateTaskId() {
444
+ return `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
445
+ }
446
+ var createTaskTool = {
447
+ name: "create_task",
448
+ description: "Create a new task in a project",
449
+ inputSchema: z2.object({
450
+ projectId: z2.string().min(1, "Project ID is required"),
451
+ title: z2.string().min(1, "Task title is required"),
452
+ description: z2.string(),
453
+ priority: TaskPriorityEnum.default("medium"),
454
+ tags: z2.array(z2.string()).default([]),
455
+ dueDate: z2.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
456
+ assignee: z2.string().optional()
457
+ }),
458
+ async execute(input) {
459
+ try {
460
+ const projectData = await storage.readProject(input.projectId);
461
+ if (!projectData) {
462
+ return {
463
+ success: false,
464
+ error: `Project with ID '${input.projectId}' not found`
465
+ };
466
+ }
467
+ const now = (/* @__PURE__ */ new Date()).toISOString();
468
+ const task = {
469
+ id: generateTaskId(),
470
+ projectId: input.projectId,
471
+ title: input.title,
472
+ description: input.description,
473
+ status: "todo",
474
+ priority: input.priority,
475
+ tags: input.tags,
476
+ dueDate: input.dueDate ?? null,
477
+ assignee: input.assignee ?? null,
478
+ createdAt: now,
479
+ updatedAt: now,
480
+ completedAt: null
481
+ };
482
+ projectData.tasks.push(task);
483
+ projectData.project.updatedAt = now;
484
+ const filePath = storage.getFilePath(input.projectId);
485
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
486
+ await writeJsonFile2(filePath, projectData);
487
+ return {
488
+ success: true,
489
+ data: task
490
+ };
491
+ } catch (error) {
492
+ return {
493
+ success: false,
494
+ error: error instanceof Error ? error.message : "Failed to create task"
495
+ };
496
+ }
497
+ }
498
+ };
499
+ var listTasksTool = {
500
+ name: "list_tasks",
501
+ description: "List tasks with optional filters",
502
+ inputSchema: z2.object({
503
+ projectId: z2.string().optional(),
504
+ status: TaskStatusEnum.optional(),
505
+ priority: TaskPriorityEnum.optional(),
506
+ tags: z2.array(z2.string()).optional(),
507
+ assignee: z2.string().optional(),
508
+ dueBefore: z2.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
509
+ dueAfter: z2.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
510
+ }),
511
+ async execute(input) {
512
+ try {
513
+ const results = await storage.searchTasks({
514
+ projectId: input.projectId,
515
+ status: input.status,
516
+ priority: input.priority,
517
+ tags: input.tags,
518
+ assignee: input.assignee,
519
+ dueBefore: input.dueBefore,
520
+ dueAfter: input.dueAfter
521
+ });
522
+ return {
523
+ success: true,
524
+ data: results
525
+ };
526
+ } catch (error) {
527
+ return {
528
+ success: false,
529
+ error: error instanceof Error ? error.message : "Failed to list tasks"
530
+ };
531
+ }
532
+ }
533
+ };
534
+ var getTaskTool = {
535
+ name: "get_task",
536
+ description: "Get a specific task by project ID and task ID",
537
+ inputSchema: z2.object({
538
+ projectId: z2.string().min(1, "Project ID is required"),
539
+ taskId: z2.string().min(1, "Task ID is required")
540
+ }),
541
+ async execute(input) {
542
+ try {
543
+ const projectData = await storage.readProject(input.projectId);
544
+ if (!projectData) {
545
+ return {
546
+ success: false,
547
+ error: `Project with ID '${input.projectId}' not found`
548
+ };
549
+ }
550
+ const task = projectData.tasks.find((t) => t.id === input.taskId);
551
+ if (!task) {
552
+ return {
553
+ success: false,
554
+ error: `Task with ID '${input.taskId}' not found in project '${input.projectId}'`
555
+ };
556
+ }
557
+ return {
558
+ success: true,
559
+ data: task
560
+ };
561
+ } catch (error) {
562
+ return {
563
+ success: false,
564
+ error: error instanceof Error ? error.message : "Failed to get task"
565
+ };
566
+ }
567
+ }
568
+ };
569
+ var updateTaskTool = {
570
+ name: "update_task",
571
+ description: "Update an existing task",
572
+ inputSchema: z2.object({
573
+ projectId: z2.string().min(1, "Project ID is required"),
574
+ taskId: z2.string().min(1, "Task ID is required"),
575
+ title: z2.string().min(1).optional(),
576
+ description: z2.string().optional(),
577
+ status: TaskStatusEnum.optional(),
578
+ priority: TaskPriorityEnum.optional(),
579
+ tags: z2.array(z2.string()).optional(),
580
+ dueDate: z2.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().nullable(),
581
+ assignee: z2.string().optional().nullable()
582
+ }),
583
+ async execute(input) {
584
+ try {
585
+ const projectData = await storage.readProject(input.projectId);
586
+ if (!projectData) {
587
+ return {
588
+ success: false,
589
+ error: `Project with ID '${input.projectId}' not found`
590
+ };
591
+ }
592
+ const taskIndex = projectData.tasks.findIndex((t) => t.id === input.taskId);
593
+ if (taskIndex === -1) {
594
+ return {
595
+ success: false,
596
+ error: `Task with ID '${input.taskId}' not found in project '${input.projectId}'`
597
+ };
598
+ }
599
+ const { projectId, taskId, ...updateData } = input;
600
+ if (Object.keys(updateData).length === 0) {
601
+ return {
602
+ success: false,
603
+ error: "At least one field to update is required"
604
+ };
605
+ }
606
+ const now = (/* @__PURE__ */ new Date()).toISOString();
607
+ const existingTask = projectData.tasks[taskIndex];
608
+ const updatedTask = {
609
+ ...existingTask,
610
+ ...updateData,
611
+ id: existingTask.id,
612
+ projectId: existingTask.projectId,
613
+ createdAt: existingTask.createdAt,
614
+ updatedAt: now,
615
+ completedAt: updateData.status === "done" && existingTask.status !== "done" ? now : updateData.status && updateData.status !== "done" ? null : existingTask.completedAt
616
+ };
617
+ projectData.tasks[taskIndex] = updatedTask;
618
+ projectData.project.updatedAt = now;
619
+ const filePath = storage.getFilePath(input.projectId);
620
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
621
+ await writeJsonFile2(filePath, projectData);
622
+ return {
623
+ success: true,
624
+ data: updatedTask
625
+ };
626
+ } catch (error) {
627
+ return {
628
+ success: false,
629
+ error: error instanceof Error ? error.message : "Failed to update task"
630
+ };
631
+ }
632
+ }
633
+ };
634
+ var deleteTaskTool = {
635
+ name: "delete_task",
636
+ description: "Delete a task by project ID and task ID",
637
+ inputSchema: z2.object({
638
+ projectId: z2.string().min(1, "Project ID is required"),
639
+ taskId: z2.string().min(1, "Task ID is required")
640
+ }),
641
+ async execute(input) {
642
+ try {
643
+ const projectData = await storage.readProject(input.projectId);
644
+ if (!projectData) {
645
+ return {
646
+ success: false,
647
+ error: `Project with ID '${input.projectId}' not found`
648
+ };
649
+ }
650
+ const taskIndex = projectData.tasks.findIndex((t) => t.id === input.taskId);
651
+ if (taskIndex === -1) {
652
+ return {
653
+ success: false,
654
+ error: `Task with ID '${input.taskId}' not found in project '${input.projectId}'`
655
+ };
656
+ }
657
+ const now = (/* @__PURE__ */ new Date()).toISOString();
658
+ projectData.tasks.splice(taskIndex, 1);
659
+ projectData.project.updatedAt = now;
660
+ const filePath = storage.getFilePath(input.projectId);
661
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
662
+ await writeJsonFile2(filePath, projectData);
663
+ return {
664
+ success: true,
665
+ data: { deleted: true }
666
+ };
667
+ } catch (error) {
668
+ return {
669
+ success: false,
670
+ error: error instanceof Error ? error.message : "Failed to delete task"
671
+ };
672
+ }
673
+ }
674
+ };
675
+ var batchUpdateTasksTool = {
676
+ name: "batch_update_tasks",
677
+ description: "Update multiple tasks at once",
678
+ inputSchema: z2.object({
679
+ projectId: z2.string().min(1, "Project ID is required"),
680
+ taskIds: z2.array(z2.string()).min(1, "At least one task ID is required"),
681
+ status: TaskStatusEnum.optional(),
682
+ priority: TaskPriorityEnum.optional(),
683
+ tags: z2.array(z2.string()).optional(),
684
+ tagOperation: z2.enum(["add", "remove", "replace"]).default("replace")
685
+ }),
686
+ async execute(input) {
687
+ try {
688
+ const projectData = await storage.readProject(input.projectId);
689
+ if (!projectData) {
690
+ return {
691
+ success: false,
692
+ error: `Project with ID '${input.projectId}' not found`
693
+ };
694
+ }
695
+ const now = (/* @__PURE__ */ new Date()).toISOString();
696
+ const updatedTasks = [];
697
+ const notFoundIds = [];
698
+ for (const taskId of input.taskIds) {
699
+ const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);
700
+ if (taskIndex === -1) {
701
+ notFoundIds.push(taskId);
702
+ continue;
703
+ }
704
+ const existingTask = projectData.tasks[taskIndex];
705
+ let updatedTags = existingTask.tags;
706
+ if (input.tags && input.tags.length > 0) {
707
+ switch (input.tagOperation) {
708
+ case "add":
709
+ updatedTags = [.../* @__PURE__ */ new Set([...existingTask.tags, ...input.tags])];
710
+ break;
711
+ case "remove":
712
+ updatedTags = existingTask.tags.filter((tag) => !input.tags.includes(tag));
713
+ break;
714
+ case "replace":
715
+ default:
716
+ updatedTags = input.tags;
717
+ break;
718
+ }
719
+ }
720
+ const updatedTask = {
721
+ ...existingTask,
722
+ ...input.status && { status: input.status },
723
+ ...input.priority && { priority: input.priority },
724
+ tags: updatedTags,
725
+ updatedAt: now,
726
+ ...input.status === "done" && existingTask.status !== "done" && { completedAt: now },
727
+ ...input.status && input.status !== "done" && { completedAt: null }
728
+ };
729
+ projectData.tasks[taskIndex] = updatedTask;
730
+ updatedTasks.push(updatedTask);
731
+ }
732
+ if (updatedTasks.length === 0) {
733
+ return {
734
+ success: false,
735
+ error: "No tasks were found to update",
736
+ notFoundIds
737
+ };
738
+ }
739
+ projectData.project.updatedAt = now;
740
+ const filePath = storage.getFilePath(input.projectId);
741
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
742
+ await writeJsonFile2(filePath, projectData);
743
+ return {
744
+ success: true,
745
+ data: {
746
+ updatedTasks,
747
+ updatedCount: updatedTasks.length,
748
+ notFoundIds: notFoundIds.length > 0 ? notFoundIds : void 0
749
+ }
750
+ };
751
+ } catch (error) {
752
+ return {
753
+ success: false,
754
+ error: error instanceof Error ? error.message : "Failed to batch update tasks"
755
+ };
756
+ }
757
+ }
758
+ };
759
+
760
+ // src/tools/tag-tools.ts
761
+ init_esm_shims();
762
+ import { z as z3 } from "zod";
763
+ function generateTagId() {
764
+ return `tag_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
765
+ }
766
+ var createTagTool = {
767
+ name: "create_tag",
768
+ description: "Create a new tag in a project",
769
+ inputSchema: z3.object({
770
+ projectId: z3.string().min(1, "Project ID is required"),
771
+ name: z3.string().min(1, "Tag name is required"),
772
+ color: z3.string().regex(/^#[0-9A-Fa-f]{6}$/, "Color must be a valid hex code (e.g., #FF5733)"),
773
+ description: z3.string().default("")
774
+ }),
775
+ async execute(input) {
776
+ try {
777
+ const projectData = await storage.readProject(input.projectId);
778
+ if (!projectData) {
779
+ return {
780
+ success: false,
781
+ error: `Project with ID '${input.projectId}' not found`
782
+ };
783
+ }
784
+ const existingTag = projectData.tags.find(
785
+ (t) => t.name.toLowerCase() === input.name.toLowerCase()
786
+ );
787
+ if (existingTag) {
788
+ return {
789
+ success: false,
790
+ error: `Tag with name '${input.name}' already exists in this project`
791
+ };
792
+ }
793
+ const now = (/* @__PURE__ */ new Date()).toISOString();
794
+ const tag = {
795
+ id: generateTagId(),
796
+ name: input.name,
797
+ color: input.color,
798
+ description: input.description,
799
+ createdAt: now
800
+ };
801
+ projectData.tags.push(tag);
802
+ projectData.project.updatedAt = now;
803
+ const filePath = storage.getFilePath(input.projectId);
804
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
805
+ await writeJsonFile2(filePath, projectData);
806
+ return {
807
+ success: true,
808
+ data: tag
809
+ };
810
+ } catch (error) {
811
+ return {
812
+ success: false,
813
+ error: error instanceof Error ? error.message : "Failed to create tag"
814
+ };
815
+ }
816
+ }
817
+ };
818
+ var listTagsTool = {
819
+ name: "list_tags",
820
+ description: "List all tags in a project",
821
+ inputSchema: z3.object({
822
+ projectId: z3.string().min(1, "Project ID is required")
823
+ }),
824
+ async execute(input) {
825
+ try {
826
+ const projectData = await storage.readProject(input.projectId);
827
+ if (!projectData) {
828
+ return {
829
+ success: false,
830
+ error: `Project with ID '${input.projectId}' not found`
831
+ };
832
+ }
833
+ return {
834
+ success: true,
835
+ data: projectData.tags
836
+ };
837
+ } catch (error) {
838
+ return {
839
+ success: false,
840
+ error: error instanceof Error ? error.message : "Failed to list tags"
841
+ };
842
+ }
843
+ }
844
+ };
845
+ var updateTagTool = {
846
+ name: "update_tag",
847
+ description: "Update an existing tag",
848
+ inputSchema: z3.object({
849
+ projectId: z3.string().min(1, "Project ID is required"),
850
+ tagId: z3.string().min(1, "Tag ID is required"),
851
+ name: z3.string().min(1).optional(),
852
+ color: z3.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
853
+ description: z3.string().optional()
854
+ }),
855
+ async execute(input) {
856
+ try {
857
+ const projectData = await storage.readProject(input.projectId);
858
+ if (!projectData) {
859
+ return {
860
+ success: false,
861
+ error: `Project with ID '${input.projectId}' not found`
862
+ };
863
+ }
864
+ const tagIndex = projectData.tags.findIndex((t) => t.id === input.tagId);
865
+ if (tagIndex === -1) {
866
+ return {
867
+ success: false,
868
+ error: `Tag with ID '${input.tagId}' not found in project '${input.projectId}'`
869
+ };
870
+ }
871
+ const { projectId, tagId, ...updateData } = input;
872
+ if (Object.keys(updateData).length === 0) {
873
+ return {
874
+ success: false,
875
+ error: "At least one field to update is required"
876
+ };
877
+ }
878
+ if (updateData.name) {
879
+ const existingTag2 = projectData.tags.find(
880
+ (t) => t.name.toLowerCase() === updateData.name.toLowerCase() && t.id !== input.tagId
881
+ );
882
+ if (existingTag2) {
883
+ return {
884
+ success: false,
885
+ error: `Tag with name '${updateData.name}' already exists in this project`
886
+ };
887
+ }
888
+ }
889
+ const now = (/* @__PURE__ */ new Date()).toISOString();
890
+ const existingTag = projectData.tags[tagIndex];
891
+ const updatedTag = {
892
+ ...existingTag,
893
+ ...updateData,
894
+ id: existingTag.id,
895
+ createdAt: existingTag.createdAt
896
+ };
897
+ projectData.tags[tagIndex] = updatedTag;
898
+ projectData.project.updatedAt = now;
899
+ const filePath = storage.getFilePath(input.projectId);
900
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
901
+ await writeJsonFile2(filePath, projectData);
902
+ return {
903
+ success: true,
904
+ data: updatedTag
905
+ };
906
+ } catch (error) {
907
+ return {
908
+ success: false,
909
+ error: error instanceof Error ? error.message : "Failed to update tag"
910
+ };
911
+ }
912
+ }
913
+ };
914
+ var deleteTagTool = {
915
+ name: "delete_tag",
916
+ description: "Delete a tag by project ID and tag ID",
917
+ inputSchema: z3.object({
918
+ projectId: z3.string().min(1, "Project ID is required"),
919
+ tagId: z3.string().min(1, "Tag ID is required")
920
+ }),
921
+ async execute(input) {
922
+ try {
923
+ const projectData = await storage.readProject(input.projectId);
924
+ if (!projectData) {
925
+ return {
926
+ success: false,
927
+ error: `Project with ID '${input.projectId}' not found`
928
+ };
929
+ }
930
+ const tagIndex = projectData.tags.findIndex((t) => t.id === input.tagId);
931
+ if (tagIndex === -1) {
932
+ return {
933
+ success: false,
934
+ error: `Tag with ID '${input.tagId}' not found in project '${input.projectId}'`
935
+ };
936
+ }
937
+ const tag = projectData.tags[tagIndex];
938
+ const now = (/* @__PURE__ */ new Date()).toISOString();
939
+ let tasksUpdated = 0;
940
+ for (const task of projectData.tasks) {
941
+ const tagIndexInTask = task.tags.indexOf(input.tagId);
942
+ if (tagIndexInTask !== -1) {
943
+ task.tags.splice(tagIndexInTask, 1);
944
+ task.updatedAt = now;
945
+ tasksUpdated++;
946
+ }
947
+ }
948
+ projectData.tags.splice(tagIndex, 1);
949
+ projectData.project.updatedAt = now;
950
+ const filePath = storage.getFilePath(input.projectId);
951
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
952
+ await writeJsonFile2(filePath, projectData);
953
+ return {
954
+ success: true,
955
+ data: {
956
+ deleted: true,
957
+ tag,
958
+ tasksUpdated
959
+ }
960
+ };
961
+ } catch (error) {
962
+ return {
963
+ success: false,
964
+ error: error instanceof Error ? error.message : "Failed to delete tag"
965
+ };
966
+ }
967
+ }
968
+ };
969
+ var getTasksByTagTool = {
970
+ name: "get_tasks_by_tag",
971
+ description: "Get all tasks that have a specific tag",
972
+ inputSchema: z3.object({
973
+ projectId: z3.string().min(1, "Project ID is required"),
974
+ tagName: z3.string().min(1, "Tag name is required")
975
+ }),
976
+ async execute(input) {
977
+ try {
978
+ const projectData = await storage.readProject(input.projectId);
979
+ if (!projectData) {
980
+ return {
981
+ success: false,
982
+ error: `Project with ID '${input.projectId}' not found`
983
+ };
984
+ }
985
+ const tag = projectData.tags.find(
986
+ (t) => t.name.toLowerCase() === input.tagName.toLowerCase()
987
+ );
988
+ if (!tag) {
989
+ return {
990
+ success: false,
991
+ error: `Tag with name '${input.tagName}' not found in project '${input.projectId}'`
992
+ };
993
+ }
994
+ const tasks = projectData.tasks.filter((t) => t.tags.includes(tag.id));
995
+ return {
996
+ success: true,
997
+ data: {
998
+ tag,
999
+ tasks,
1000
+ count: tasks.length
1001
+ }
1002
+ };
1003
+ } catch (error) {
1004
+ return {
1005
+ success: false,
1006
+ error: error instanceof Error ? error.message : "Failed to get tasks by tag"
1007
+ };
1008
+ }
1009
+ }
1010
+ };
1011
+
1012
+ // src/tools/web-tools.ts
1013
+ init_esm_shims();
1014
+
1015
+ // src/web/server.ts
1016
+ init_esm_shims();
1017
+ import express from "express";
1018
+ import * as path4 from "path";
1019
+ import { fileURLToPath as fileURLToPath2 } from "url";
1020
+ var __filename2 = fileURLToPath2(import.meta.url);
1021
+ var __dirname2 = path4.dirname(__filename2);
1022
+ function createServer(port = 7860) {
1023
+ const app = express();
1024
+ app.use(express.json());
1025
+ app.get("/api/projects", async (_req, res) => {
1026
+ try {
1027
+ const projects = await storage.listProjects();
1028
+ res.json(projects);
1029
+ } catch (error) {
1030
+ res.status(500).json({ error: error.message });
1031
+ }
1032
+ });
1033
+ app.get("/api/projects/:id", async (req, res) => {
1034
+ try {
1035
+ const project = await storage.readProject(req.params.id);
1036
+ if (!project) {
1037
+ res.status(404).json({ error: "Project not found" });
1038
+ return;
1039
+ }
1040
+ res.json(project);
1041
+ } catch (error) {
1042
+ res.status(500).json({ error: error.message });
1043
+ }
1044
+ });
1045
+ app.get("/api/tasks", async (req, res) => {
1046
+ try {
1047
+ const filters = req.query;
1048
+ const tasks = await storage.searchTasks(filters);
1049
+ res.json(tasks);
1050
+ } catch (error) {
1051
+ res.status(500).json({ error: error.message });
1052
+ }
1053
+ });
1054
+ app.post("/api/projects", async (req, res) => {
1055
+ try {
1056
+ const project = await storage.createProject(req.body);
1057
+ res.json({ success: true, data: project });
1058
+ } catch (error) {
1059
+ res.status(500).json({ error: error.message });
1060
+ }
1061
+ });
1062
+ app.put("/api/projects", async (req, res) => {
1063
+ try {
1064
+ const { projectId, ...updateData } = req.body;
1065
+ const project = await storage.updateProject(projectId, updateData);
1066
+ res.json({ success: true, data: project });
1067
+ } catch (error) {
1068
+ res.status(500).json({ error: error.message });
1069
+ }
1070
+ });
1071
+ app.delete("/api/projects", async (req, res) => {
1072
+ try {
1073
+ const { projectId } = req.query;
1074
+ await storage.deleteProject(projectId);
1075
+ res.json({ success: true });
1076
+ } catch (error) {
1077
+ res.status(500).json({ error: error.message });
1078
+ }
1079
+ });
1080
+ app.post("/api/tasks", async (req, res) => {
1081
+ try {
1082
+ const { projectId, ...taskData } = req.body;
1083
+ const projectData = await storage.readProject(projectId);
1084
+ if (!projectData) {
1085
+ res.status(404).json({ error: "Project not found" });
1086
+ return;
1087
+ }
1088
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1089
+ const task = {
1090
+ id: `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
1091
+ projectId,
1092
+ ...taskData,
1093
+ status: taskData.status || "todo",
1094
+ priority: taskData.priority || "medium",
1095
+ tags: taskData.tags || [],
1096
+ dueDate: taskData.dueDate || null,
1097
+ assignee: taskData.assignee || null,
1098
+ createdAt: now,
1099
+ updatedAt: now,
1100
+ completedAt: null
1101
+ };
1102
+ projectData.tasks.push(task);
1103
+ projectData.project.updatedAt = now;
1104
+ const filePath = storage.getFilePath(projectId);
1105
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
1106
+ await writeJsonFile2(filePath, projectData);
1107
+ res.json({ success: true, data: task });
1108
+ } catch (error) {
1109
+ res.status(500).json({ error: error.message });
1110
+ }
1111
+ });
1112
+ app.put("/api/tasks", async (req, res) => {
1113
+ try {
1114
+ const { projectId, taskId, ...updateData } = req.body;
1115
+ const projectData = await storage.readProject(projectId);
1116
+ if (!projectData) {
1117
+ res.status(404).json({ error: "Project not found" });
1118
+ return;
1119
+ }
1120
+ const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);
1121
+ if (taskIndex === -1) {
1122
+ res.status(404).json({ error: "Task not found" });
1123
+ return;
1124
+ }
1125
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1126
+ const existingTask = projectData.tasks[taskIndex];
1127
+ const updatedTask = {
1128
+ ...existingTask,
1129
+ ...updateData,
1130
+ id: existingTask.id,
1131
+ projectId: existingTask.projectId,
1132
+ createdAt: existingTask.createdAt,
1133
+ updatedAt: now,
1134
+ completedAt: updateData.status === "done" && existingTask.status !== "done" ? now : updateData.status && updateData.status !== "done" ? null : existingTask.completedAt
1135
+ };
1136
+ projectData.tasks[taskIndex] = updatedTask;
1137
+ projectData.project.updatedAt = now;
1138
+ const filePath = storage.getFilePath(projectId);
1139
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
1140
+ await writeJsonFile2(filePath, projectData);
1141
+ res.json({ success: true, data: updatedTask });
1142
+ } catch (error) {
1143
+ res.status(500).json({ error: error.message });
1144
+ }
1145
+ });
1146
+ app.delete("/api/tasks", async (req, res) => {
1147
+ try {
1148
+ const { projectId, taskId } = req.query;
1149
+ const projectData = await storage.readProject(projectId);
1150
+ if (!projectData) {
1151
+ res.status(404).json({ error: "Project not found" });
1152
+ return;
1153
+ }
1154
+ const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);
1155
+ if (taskIndex === -1) {
1156
+ res.status(404).json({ error: "Task not found" });
1157
+ return;
1158
+ }
1159
+ projectData.tasks.splice(taskIndex, 1);
1160
+ projectData.project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1161
+ const filePath = storage.getFilePath(projectId);
1162
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
1163
+ await writeJsonFile2(filePath, projectData);
1164
+ res.json({ success: true });
1165
+ } catch (error) {
1166
+ res.status(500).json({ error: error.message });
1167
+ }
1168
+ });
1169
+ const distPath = path4.join(__dirname2, "app");
1170
+ app.use(express.static(distPath));
1171
+ app.get("*", (req, res) => {
1172
+ if (req.path.startsWith("/api")) {
1173
+ res.status(404).json({ error: "API not found" });
1174
+ return;
1175
+ }
1176
+ res.sendFile(path4.join(distPath, "index.html"));
1177
+ });
1178
+ const server = app.listen(port, "0.0.0.0", () => {
1179
+ console.log(`Web interface server running at http://0.0.0.0:${port}`);
1180
+ });
1181
+ return server;
1182
+ }
1183
+
1184
+ // src/tools/web-tools.ts
1185
+ var activeServer = null;
1186
+ var openWebInterfaceTool = {
1187
+ name: "open_web_interface",
1188
+ description: "Open web visualization interface",
1189
+ parameters: {
1190
+ type: "object",
1191
+ properties: {
1192
+ port: {
1193
+ type: "number",
1194
+ description: "Port to run the web interface on (default: 7860)"
1195
+ }
1196
+ }
1197
+ },
1198
+ async execute(args) {
1199
+ if (activeServer) {
1200
+ return {
1201
+ message: "Web interface is already running",
1202
+ url: `http://localhost:${activeServer.address().port}`
1203
+ };
1204
+ }
1205
+ const port = args.port || 7860;
1206
+ activeServer = createServer(port);
1207
+ return {
1208
+ message: "Web interface started successfully",
1209
+ url: `http://localhost:${port}`
1210
+ };
1211
+ }
1212
+ };
1213
+ var closeWebInterfaceTool = {
1214
+ name: "close_web_interface",
1215
+ description: "Close web visualization interface",
1216
+ async execute() {
1217
+ if (!activeServer) {
1218
+ return { message: "Web interface is not running" };
1219
+ }
1220
+ return new Promise((resolve) => {
1221
+ activeServer.close(() => {
1222
+ activeServer = null;
1223
+ resolve({ message: "Web interface stopped" });
1224
+ });
1225
+ });
1226
+ }
1227
+ };
1228
+
1229
+ // src/tools/template-tools.ts
1230
+ init_esm_shims();
1231
+ import { z as z4 } from "zod";
1232
+ import * as path5 from "path";
1233
+ import * as fs2 from "fs/promises";
1234
+ var TEMPLATES_DIR = path5.join(process.cwd(), "templates");
1235
+ async function getTemplateFiles() {
1236
+ try {
1237
+ const files = await fs2.readdir(TEMPLATES_DIR);
1238
+ return files.filter((f) => f.endsWith(".json"));
1239
+ } catch {
1240
+ return [];
1241
+ }
1242
+ }
1243
+ async function loadTemplate(templateName) {
1244
+ try {
1245
+ const fileName = templateName.endsWith(".json") ? templateName : `${templateName}.json`;
1246
+ const filePath = path5.join(TEMPLATES_DIR, fileName);
1247
+ const content = await fs2.readFile(filePath, "utf-8");
1248
+ return JSON.parse(content);
1249
+ } catch {
1250
+ return null;
1251
+ }
1252
+ }
1253
+ function generateId(prefix) {
1254
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
1255
+ }
1256
+ var listTemplatesTool = {
1257
+ name: "list_templates",
1258
+ description: "List all available project templates",
1259
+ inputSchema: z4.object({}),
1260
+ async execute() {
1261
+ try {
1262
+ const templateFiles = await getTemplateFiles();
1263
+ const templates = [];
1264
+ for (const file of templateFiles) {
1265
+ const template = await loadTemplate(file);
1266
+ if (template) {
1267
+ templates.push({
1268
+ name: file.replace(".json", ""),
1269
+ displayName: template.name,
1270
+ description: template.description,
1271
+ projectType: template.projectType,
1272
+ taskCount: template.tasks.length,
1273
+ tagCount: template.tags.length
1274
+ });
1275
+ }
1276
+ }
1277
+ return {
1278
+ success: true,
1279
+ data: templates
1280
+ };
1281
+ } catch (error) {
1282
+ return {
1283
+ success: false,
1284
+ error: error instanceof Error ? error.message : "Failed to list templates"
1285
+ };
1286
+ }
1287
+ }
1288
+ };
1289
+ var getTemplateTool = {
1290
+ name: "get_template",
1291
+ description: "Get detailed information about a specific template",
1292
+ inputSchema: z4.object({
1293
+ templateName: z4.string().min(1, "Template name is required")
1294
+ }),
1295
+ async execute(input) {
1296
+ try {
1297
+ const template = await loadTemplate(input.templateName);
1298
+ if (!template) {
1299
+ return {
1300
+ success: false,
1301
+ error: `Template '${input.templateName}' not found`
1302
+ };
1303
+ }
1304
+ return {
1305
+ success: true,
1306
+ data: {
1307
+ name: template.name,
1308
+ description: template.description,
1309
+ projectType: template.projectType,
1310
+ tasks: template.tasks.map((t) => ({
1311
+ title: t.title,
1312
+ description: t.description,
1313
+ priority: t.priority,
1314
+ tags: t.tags,
1315
+ estimatedHours: t.estimatedHours
1316
+ })),
1317
+ tags: template.tags
1318
+ }
1319
+ };
1320
+ } catch (error) {
1321
+ return {
1322
+ success: false,
1323
+ error: error instanceof Error ? error.message : "Failed to get template"
1324
+ };
1325
+ }
1326
+ }
1327
+ };
1328
+ var applyTemplateTool = {
1329
+ name: "apply_template",
1330
+ description: "Create a new project from a template",
1331
+ inputSchema: z4.object({
1332
+ templateName: z4.string().min(1, "Template name is required"),
1333
+ projectName: z4.string().min(1, "Project name is required"),
1334
+ description: z4.string().default(""),
1335
+ startDate: z4.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").optional(),
1336
+ targetDate: z4.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").optional()
1337
+ }),
1338
+ async execute(input) {
1339
+ try {
1340
+ const template = await loadTemplate(input.templateName);
1341
+ if (!template) {
1342
+ return {
1343
+ success: false,
1344
+ error: `Template '${input.templateName}' not found`
1345
+ };
1346
+ }
1347
+ const projectInput = {
1348
+ name: input.projectName,
1349
+ description: input.description || template.description,
1350
+ projectType: template.projectType,
1351
+ startDate: input.startDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
1352
+ targetDate: input.targetDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
1353
+ };
1354
+ const projectData = await storage.createProject(projectInput);
1355
+ const projectId = projectData.project.id;
1356
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1357
+ const tagNameToId = /* @__PURE__ */ new Map();
1358
+ const tags = [];
1359
+ for (const templateTag of template.tags) {
1360
+ const tag = {
1361
+ id: generateId("tag"),
1362
+ name: templateTag.name,
1363
+ color: templateTag.color,
1364
+ description: "",
1365
+ createdAt: now
1366
+ };
1367
+ tags.push(tag);
1368
+ tagNameToId.set(templateTag.name, tag.id);
1369
+ }
1370
+ const tasks = [];
1371
+ for (const templateTask of template.tasks) {
1372
+ const taskTagIds = templateTask.tags.map((tagName) => tagNameToId.get(tagName)).filter((id) => id !== void 0);
1373
+ const task = {
1374
+ id: generateId("task"),
1375
+ projectId,
1376
+ title: templateTask.title,
1377
+ description: templateTask.description,
1378
+ status: "todo",
1379
+ priority: templateTask.priority,
1380
+ tags: taskTagIds,
1381
+ dueDate: null,
1382
+ assignee: null,
1383
+ createdAt: now,
1384
+ updatedAt: now,
1385
+ completedAt: null
1386
+ };
1387
+ tasks.push(task);
1388
+ }
1389
+ projectData.tags = tags;
1390
+ projectData.tasks = tasks;
1391
+ projectData.project.updatedAt = now;
1392
+ const filePath = storage.getFilePath(projectId);
1393
+ const { writeJsonFile: writeJsonFile2 } = await Promise.resolve().then(() => (init_file_helpers(), file_helpers_exports));
1394
+ await writeJsonFile2(filePath, projectData);
1395
+ return {
1396
+ success: true,
1397
+ data: {
1398
+ project: projectData.project,
1399
+ taskCount: tasks.length,
1400
+ tagCount: tags.length,
1401
+ tasksCreated: tasks.map((t) => ({
1402
+ id: t.id,
1403
+ title: t.title,
1404
+ priority: t.priority
1405
+ })),
1406
+ tagsCreated: tags.map((t) => ({
1407
+ id: t.id,
1408
+ name: t.name,
1409
+ color: t.color
1410
+ }))
1411
+ }
1412
+ };
1413
+ } catch (error) {
1414
+ return {
1415
+ success: false,
1416
+ error: error instanceof Error ? error.message : "Failed to apply template"
1417
+ };
1418
+ }
1419
+ }
1420
+ };
1421
+
1422
+ // src/resources/index.ts
1423
+ init_esm_shims();
1424
+
1425
+ // src/resources/project-resources.ts
1426
+ init_esm_shims();
1427
+ var projectResources = [
1428
+ {
1429
+ uri: "roadmap://projects",
1430
+ name: "Project List",
1431
+ mimeType: "application/json",
1432
+ description: "Returns a list of all projects with basic metadata"
1433
+ },
1434
+ {
1435
+ uri: "roadmap://project/{projectId}",
1436
+ name: "Project Details",
1437
+ mimeType: "application/json",
1438
+ description: "Returns detailed information about a specific project including tasks, milestones, and tags"
1439
+ },
1440
+ {
1441
+ uri: "roadmap://project/{projectId}/tasks",
1442
+ name: "Project Tasks",
1443
+ mimeType: "application/json",
1444
+ description: "Returns all tasks for a specific project"
1445
+ },
1446
+ {
1447
+ uri: "roadmap://project/{projectId}/progress",
1448
+ name: "Project Progress",
1449
+ mimeType: "application/json",
1450
+ description: "Returns progress statistics for a specific project"
1451
+ }
1452
+ ];
1453
+ var RESOURCE_PATTERNS = {
1454
+ projectList: /^roadmap:\/\/projects$/,
1455
+ projectDetails: /^roadmap:\/\/project\/([^/]+)$/,
1456
+ projectTasks: /^roadmap:\/\/project\/([^/]+)\/tasks$/,
1457
+ projectProgress: /^roadmap:\/\/project\/([^/]+)\/progress$/
1458
+ };
1459
+ async function handleResourceRequest(uri) {
1460
+ if (RESOURCE_PATTERNS.projectList.test(uri)) {
1461
+ return await getProjectListResource(uri);
1462
+ }
1463
+ const projectDetailsMatch = uri.match(RESOURCE_PATTERNS.projectDetails);
1464
+ if (projectDetailsMatch && !uri.includes("/tasks") && !uri.includes("/progress")) {
1465
+ const projectId = projectDetailsMatch[1];
1466
+ return await getProjectDetailsResource(uri, projectId);
1467
+ }
1468
+ const projectTasksMatch = uri.match(RESOURCE_PATTERNS.projectTasks);
1469
+ if (projectTasksMatch) {
1470
+ const projectId = projectTasksMatch[1];
1471
+ return await getProjectTasksResource(uri, projectId);
1472
+ }
1473
+ const projectProgressMatch = uri.match(RESOURCE_PATTERNS.projectProgress);
1474
+ if (projectProgressMatch) {
1475
+ const projectId = projectProgressMatch[1];
1476
+ return await getProjectProgressResource(uri, projectId);
1477
+ }
1478
+ return null;
1479
+ }
1480
+ async function getProjectListResource(uri) {
1481
+ const projects = await storage.listProjects();
1482
+ const data = {
1483
+ projects: projects.map((p) => ({
1484
+ id: p.project.id,
1485
+ name: p.project.name,
1486
+ description: p.project.description,
1487
+ status: p.project.status,
1488
+ projectType: p.project.projectType,
1489
+ taskCount: p.taskCount,
1490
+ milestoneCount: p.milestoneCount,
1491
+ updatedAt: p.project.updatedAt
1492
+ })),
1493
+ totalCount: projects.length
1494
+ };
1495
+ return {
1496
+ uri,
1497
+ mimeType: "application/json",
1498
+ text: JSON.stringify(data, null, 2)
1499
+ };
1500
+ }
1501
+ async function getProjectDetailsResource(uri, projectId) {
1502
+ const projectData = await storage.readProject(projectId);
1503
+ if (!projectData) {
1504
+ return null;
1505
+ }
1506
+ const data = {
1507
+ project: projectData.project,
1508
+ milestones: projectData.milestones,
1509
+ tasks: projectData.tasks,
1510
+ tags: projectData.tags,
1511
+ stats: {
1512
+ taskCount: projectData.tasks.length,
1513
+ milestoneCount: projectData.milestones.length,
1514
+ tagCount: projectData.tags.length,
1515
+ completedTasks: projectData.tasks.filter((t) => t.status === "done").length,
1516
+ inProgressTasks: projectData.tasks.filter((t) => t.status === "in-progress").length
1517
+ }
1518
+ };
1519
+ return {
1520
+ uri,
1521
+ mimeType: "application/json",
1522
+ text: JSON.stringify(data, null, 2)
1523
+ };
1524
+ }
1525
+ async function getProjectTasksResource(uri, projectId) {
1526
+ const projectData = await storage.readProject(projectId);
1527
+ if (!projectData) {
1528
+ return null;
1529
+ }
1530
+ const tasksByStatus = {
1531
+ todo: projectData.tasks.filter((t) => t.status === "todo"),
1532
+ inProgress: projectData.tasks.filter((t) => t.status === "in-progress"),
1533
+ review: projectData.tasks.filter((t) => t.status === "review"),
1534
+ done: projectData.tasks.filter((t) => t.status === "done")
1535
+ };
1536
+ const data = {
1537
+ projectId,
1538
+ projectName: projectData.project.name,
1539
+ tasks: projectData.tasks,
1540
+ tasksByStatus,
1541
+ summary: {
1542
+ total: projectData.tasks.length,
1543
+ todo: tasksByStatus.todo.length,
1544
+ inProgress: tasksByStatus.inProgress.length,
1545
+ review: tasksByStatus.review.length,
1546
+ done: tasksByStatus.done.length
1547
+ }
1548
+ };
1549
+ return {
1550
+ uri,
1551
+ mimeType: "application/json",
1552
+ text: JSON.stringify(data, null, 2)
1553
+ };
1554
+ }
1555
+ async function getProjectProgressResource(uri, projectId) {
1556
+ const projectData = await storage.readProject(projectId);
1557
+ if (!projectData) {
1558
+ return null;
1559
+ }
1560
+ const tasks = projectData.tasks;
1561
+ const totalTasks = tasks.length;
1562
+ const completedTasks = tasks.filter((t) => t.status === "done").length;
1563
+ const inProgressTasks = tasks.filter((t) => t.status === "in-progress").length;
1564
+ const reviewTasks = tasks.filter((t) => t.status === "review").length;
1565
+ const todoTasks = tasks.filter((t) => t.status === "todo").length;
1566
+ const completionPercentage = totalTasks > 0 ? Math.round(completedTasks / totalTasks * 100) : 0;
1567
+ const milestones = projectData.milestones;
1568
+ const completedMilestones = milestones.filter((m) => m.completedAt !== null).length;
1569
+ const milestoneProgress = milestones.length > 0 ? Math.round(completedMilestones / milestones.length * 100) : 0;
1570
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1571
+ const overdueTasks = tasks.filter(
1572
+ (t) => t.dueDate && t.dueDate < now && t.status !== "done"
1573
+ );
1574
+ const priorityBreakdown = {
1575
+ critical: tasks.filter((t) => t.priority === "critical").length,
1576
+ high: tasks.filter((t) => t.priority === "high").length,
1577
+ medium: tasks.filter((t) => t.priority === "medium").length,
1578
+ low: tasks.filter((t) => t.priority === "low").length
1579
+ };
1580
+ const data = {
1581
+ projectId,
1582
+ projectName: projectData.project.name,
1583
+ projectStatus: projectData.project.status,
1584
+ dates: {
1585
+ startDate: projectData.project.startDate,
1586
+ targetDate: projectData.project.targetDate,
1587
+ daysRemaining: calculateDaysRemaining(projectData.project.targetDate)
1588
+ },
1589
+ taskProgress: {
1590
+ total: totalTasks,
1591
+ completed: completedTasks,
1592
+ inProgress: inProgressTasks,
1593
+ review: reviewTasks,
1594
+ todo: todoTasks,
1595
+ completionPercentage
1596
+ },
1597
+ milestoneProgress: {
1598
+ total: milestones.length,
1599
+ completed: completedMilestones,
1600
+ percentage: milestoneProgress
1601
+ },
1602
+ overdueTasks: {
1603
+ count: overdueTasks.length,
1604
+ tasks: overdueTasks.map((t) => ({
1605
+ id: t.id,
1606
+ title: t.title,
1607
+ dueDate: t.dueDate,
1608
+ status: t.status
1609
+ }))
1610
+ },
1611
+ priorityBreakdown,
1612
+ lastUpdated: projectData.project.updatedAt
1613
+ };
1614
+ return {
1615
+ uri,
1616
+ mimeType: "application/json",
1617
+ text: JSON.stringify(data, null, 2)
1618
+ };
1619
+ }
1620
+ function calculateDaysRemaining(targetDate) {
1621
+ const target = new Date(targetDate);
1622
+ const now = /* @__PURE__ */ new Date();
1623
+ const diffTime = target.getTime() - now.getTime();
1624
+ const diffDays = Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
1625
+ return diffDays > 0 ? diffDays : null;
1626
+ }
1627
+ function getAllResources() {
1628
+ return projectResources;
1629
+ }
1630
+
1631
+ // src/prompts/index.ts
1632
+ init_esm_shims();
1633
+
1634
+ // src/prompts/project-prompts.ts
1635
+ init_esm_shims();
1636
+ var projectPrompts = [
1637
+ {
1638
+ name: "projectPlanningPrompt",
1639
+ description: "Helps users plan a new project with structured guidance",
1640
+ arguments: [
1641
+ {
1642
+ name: "projectType",
1643
+ description: "Type of project (roadmap, skill-tree, kanban)",
1644
+ required: false
1645
+ }
1646
+ ]
1647
+ },
1648
+ {
1649
+ name: "taskManagementPrompt",
1650
+ description: "Helps users manage and organize tasks effectively",
1651
+ arguments: [
1652
+ {
1653
+ name: "projectId",
1654
+ description: "ID of the project to manage tasks for",
1655
+ required: false
1656
+ }
1657
+ ]
1658
+ },
1659
+ {
1660
+ name: "roadmapOverviewPrompt",
1661
+ description: "Shows a comprehensive overview of the roadmap",
1662
+ arguments: []
1663
+ },
1664
+ {
1665
+ name: "milestoneReviewPrompt",
1666
+ description: "Helps review and evaluate project milestones",
1667
+ arguments: [
1668
+ {
1669
+ name: "projectId",
1670
+ description: "ID of the project to review milestones for",
1671
+ required: false
1672
+ }
1673
+ ]
1674
+ }
1675
+ ];
1676
+ function getProjectPlanningPrompt(projectType) {
1677
+ const typeContext = projectType ? `You are planning a ${projectType} project.` : "You are planning a new project.";
1678
+ return {
1679
+ description: "Project Planning Assistant",
1680
+ messages: [
1681
+ {
1682
+ role: "user",
1683
+ content: {
1684
+ type: "text",
1685
+ text: `${typeContext}
1686
+
1687
+ I will help you plan your project step by step. Let's start by defining the key aspects:
1688
+
1689
+ ## Step 1: Project Definition
1690
+ - What is the name of your project?
1691
+ - What is the main goal or objective?
1692
+ - Who is the target audience or beneficiary?
1693
+
1694
+ ## Step 2: Scope and Timeline
1695
+ - What are the start and target completion dates?
1696
+ - What are the major deliverables?
1697
+ - What is explicitly out of scope?
1698
+
1699
+ ## Step 3: Key Milestones
1700
+ Identify 3-5 major milestones that mark significant progress:
1701
+ - Milestone 1: [Name] - Target Date
1702
+ - Milestone 2: [Name] - Target Date
1703
+ - Milestone 3: [Name] - Target Date
1704
+
1705
+ ## Step 4: Initial Tasks
1706
+ List the first 5-10 tasks to get started:
1707
+ - Task name and brief description
1708
+ - Priority level (low, medium, high, critical)
1709
+ - Estimated completion time
1710
+
1711
+ ## Step 5: Resources and Tags
1712
+ - What categories or tags will help organize this project?
1713
+ - Are there any specific resources, tools, or people needed?
1714
+
1715
+ ## Example
1716
+ Here's a simple example for a "Learn TypeScript" skill-tree project:
1717
+
1718
+ **Project**: TypeScript Mastery
1719
+ **Goal**: Become proficient in TypeScript for web development
1720
+ **Timeline**: 3 months (Jan 1 - Mar 31)
1721
+
1722
+ **Milestones**:
1723
+ 1. Complete basic syntax (Week 2)
1724
+ 2. Build first TypeScript app (Week 6)
1725
+ 3. Master advanced types (Week 10)
1726
+ 4. Complete final project (Week 12)
1727
+
1728
+ **Initial Tasks**:
1729
+ 1. Set up TypeScript environment (high priority)
1730
+ 2. Complete "Hello World" tutorial (medium priority)
1731
+ 3. Study type annotations (high priority)
1732
+ 4. Practice with interfaces (medium priority)
1733
+
1734
+ Please share your project details, and I'll help you refine the plan!`
1735
+ }
1736
+ }
1737
+ ]
1738
+ };
1739
+ }
1740
+ function getTaskManagementPrompt(projectId) {
1741
+ const projectContext = projectId ? `Managing tasks for project: ${projectId}` : "Managing tasks across all projects";
1742
+ return {
1743
+ description: "Task Management Assistant",
1744
+ messages: [
1745
+ {
1746
+ role: "user",
1747
+ content: {
1748
+ type: "text",
1749
+ text: `${projectContext}
1750
+
1751
+ I'll help you manage your tasks effectively. Here are the key areas we can work on:
1752
+
1753
+ ## Task Organization
1754
+
1755
+ ### Current Status Review
1756
+ Let's review tasks by status:
1757
+ - **Todo**: Tasks waiting to be started
1758
+ - **In Progress**: Tasks currently being worked on
1759
+ - **Review**: Tasks completed and awaiting review
1760
+ - **Done**: Completed tasks
1761
+
1762
+ ### Priority Management
1763
+ Tasks should be prioritized as:
1764
+ - **Critical**: Blockers, urgent deadlines, essential functionality
1765
+ - **High**: Important features, near-term deadlines
1766
+ - **Medium**: Standard work, nice-to-have features
1767
+ - **Low**: Backlog items, future improvements
1768
+
1769
+ ## Task Actions
1770
+
1771
+ ### Creating New Tasks
1772
+ When creating a task, include:
1773
+ - Clear, actionable title
1774
+ - Detailed description with acceptance criteria
1775
+ - Priority level
1776
+ - Due date (if applicable)
1777
+ - Assignee (if applicable)
1778
+ - Relevant tags
1779
+
1780
+ ### Updating Tasks
1781
+ Common updates include:
1782
+ - Status changes (todo \u2192 in progress \u2192 review \u2192 done)
1783
+ - Priority adjustments
1784
+ - Due date modifications
1785
+ - Adding comments or notes
1786
+
1787
+ ### Batch Operations
1788
+ You can perform actions on multiple tasks:
1789
+ - Update status for multiple tasks
1790
+ - Reassign tasks
1791
+ - Apply tags to multiple tasks
1792
+
1793
+ ## Best Practices
1794
+
1795
+ 1. **Keep tasks small**: Break large work into manageable pieces
1796
+ 2. **Write clear titles**: Use action verbs (e.g., "Implement", "Fix", "Update")
1797
+ 3. **Set realistic due dates**: Account for dependencies and blockers
1798
+ 4. **Review regularly**: Check overdue tasks and adjust priorities
1799
+ 5. **Use tags consistently**: Create a tagging convention and stick to it
1800
+
1801
+ ## Example Workflow
1802
+
1803
+ **Morning Routine**:
1804
+ 1. Review overdue tasks
1805
+ 2. Check what's in progress
1806
+ 3. Prioritize today's work
1807
+ 4. Update task statuses
1808
+
1809
+ **Weekly Review**:
1810
+ 1. Review completed tasks
1811
+ 2. Assess progress toward milestones
1812
+ 3. Reprioritize upcoming work
1813
+ 4. Identify blockers
1814
+
1815
+ What would you like to work on? You can:
1816
+ - View tasks by status
1817
+ - Create new tasks
1818
+ - Update existing tasks
1819
+ - Search for specific tasks
1820
+ - Get an overview of task distribution`
1821
+ }
1822
+ }
1823
+ ]
1824
+ };
1825
+ }
1826
+ function getRoadmapOverviewPrompt() {
1827
+ return {
1828
+ description: "Roadmap Overview Assistant",
1829
+ messages: [
1830
+ {
1831
+ role: "user",
1832
+ content: {
1833
+ type: "text",
1834
+ text: `Welcome to your Roadmap Overview!
1835
+
1836
+ I'll help you get a comprehensive view of all your projects and their progress. Here's what we can explore:
1837
+
1838
+ ## Dashboard Overview
1839
+
1840
+ ### Project Summary
1841
+ - Total number of projects
1842
+ - Projects by status (active, completed, archived)
1843
+ - Projects by type (roadmap, skill-tree, kanban)
1844
+ - Recently updated projects
1845
+
1846
+ ### Task Overview
1847
+ - Total tasks across all projects
1848
+ - Tasks by status (todo, in progress, review, done)
1849
+ - Overdue tasks requiring attention
1850
+ - Tasks by priority level
1851
+
1852
+ ### Progress Metrics
1853
+ - Overall completion percentage
1854
+ - Milestones completed vs. total
1855
+ - Average tasks per project
1856
+ - Upcoming deadlines
1857
+
1858
+ ## Project Health Indicators
1859
+
1860
+ ### Green (Healthy)
1861
+ - On track with timeline
1862
+ - No overdue critical tasks
1863
+ - Regular progress being made
1864
+
1865
+ ### Yellow (Attention Needed)
1866
+ - Some tasks overdue but not critical
1867
+ - Approaching deadline
1868
+ - Tasks stuck in review
1869
+
1870
+ ### Red (Requires Action)
1871
+ - Critical tasks overdue
1872
+ - Missed milestones
1873
+ - No recent progress
1874
+
1875
+ ## Navigation Guide
1876
+
1877
+ ### Viewing Projects
1878
+ - List all projects with key metrics
1879
+ - Filter by status or type
1880
+ - Sort by various criteria (updated, created, name)
1881
+
1882
+ ### Drilling Down
1883
+ - View specific project details
1884
+ - See all tasks for a project
1885
+ - Check milestone progress
1886
+ - Review tag usage
1887
+
1888
+ ### Taking Action
1889
+ - Identify projects needing attention
1890
+ - Find overdue tasks
1891
+ - Review completed work
1892
+ - Plan upcoming work
1893
+
1894
+ ## Example Queries
1895
+
1896
+ **"Show me all active projects"**
1897
+ Lists projects with status = active, sorted by most recently updated
1898
+
1899
+ **"What tasks are overdue?"**
1900
+ Shows all tasks where due date < today and status != done
1901
+
1902
+ **"Which projects are at risk?"**
1903
+ Identifies projects with overdue critical/high priority tasks
1904
+
1905
+ **"Show my progress this week"**
1906
+ Displays tasks completed in the last 7 days
1907
+
1908
+ ## Quick Actions
1909
+
1910
+ 1. **Review Overdue Items**: Check what needs immediate attention
1911
+ 2. **Celebrate Wins**: Review recently completed tasks
1912
+ 3. **Plan Ahead**: Look at upcoming milestones and deadlines
1913
+ 4. **Clean Up**: Archive completed projects
1914
+
1915
+ What would you like to see first? I can show you:
1916
+ - A summary of all projects
1917
+ - Overdue tasks across all projects
1918
+ - Progress statistics
1919
+ - Specific project details`
1920
+ }
1921
+ }
1922
+ ]
1923
+ };
1924
+ }
1925
+ function getMilestoneReviewPrompt(projectId) {
1926
+ const projectContext = projectId ? `Reviewing milestones for project: ${projectId}` : "Reviewing milestones across all projects";
1927
+ return {
1928
+ description: "Milestone Review Assistant",
1929
+ messages: [
1930
+ {
1931
+ role: "user",
1932
+ content: {
1933
+ type: "text",
1934
+ text: `${projectContext}
1935
+
1936
+ I'll help you review and evaluate your project milestones. Milestones are key checkpoints that help track significant progress.
1937
+
1938
+ ## Milestone Review Framework
1939
+
1940
+ ### What is a Milestone?
1941
+ A milestone represents a significant achievement or checkpoint in your project:
1942
+ - Major deliverable completion
1943
+ - Phase transition
1944
+ - Key decision point
1945
+ - External deadline
1946
+
1947
+ ### Milestone Status Types
1948
+ - **Not Started**: Target date in future, no work completed
1949
+ - **In Progress**: Work ongoing toward milestone
1950
+ - **At Risk**: May not meet target date
1951
+ - **Completed**: Achieved and marked complete
1952
+ - **Missed**: Target date passed without completion
1953
+
1954
+ ## Review Checklist
1955
+
1956
+ ### For Each Milestone, Ask:
1957
+
1958
+ 1. **Relevance**
1959
+ - Is this milestone still relevant to project goals?
1960
+ - Does it represent meaningful progress?
1961
+ - Are the success criteria clear?
1962
+
1963
+ 2. **Timeline**
1964
+ - Is the target date realistic?
1965
+ - Are there dependencies blocking progress?
1966
+ - Do we need to adjust the date?
1967
+
1968
+ 3. **Progress**
1969
+ - What percentage complete is this milestone?
1970
+ - Which tasks contribute to this milestone?
1971
+ - Are there blockers or risks?
1972
+
1973
+ 4. **Completion Criteria**
1974
+ - What defines "complete" for this milestone?
1975
+ - Are there acceptance criteria?
1976
+ - Who needs to sign off?
1977
+
1978
+ ## Milestone Management Actions
1979
+
1980
+ ### Creating Milestones
1981
+ Best practices for new milestones:
1982
+ - Use clear, descriptive names
1983
+ - Set realistic target dates
1984
+ - Define completion criteria
1985
+ - Link related tasks
1986
+
1987
+ ### Updating Milestones
1988
+ Common updates during review:
1989
+ - Adjust target dates based on progress
1990
+ - Update descriptions to reflect scope changes
1991
+ - Mark completed when criteria met
1992
+ - Archive obsolete milestones
1993
+
1994
+ ### Tracking Progress
1995
+ Ways to monitor milestone health:
1996
+ - Percentage of linked tasks complete
1997
+ - Time remaining vs. work remaining
1998
+ - Risk assessment (low/medium/high)
1999
+ - Dependency status
2000
+
2001
+ ## Example Milestone Review
2002
+
2003
+ **Project**: Website Redesign
2004
+ **Milestone**: Design Phase Complete
2005
+ **Target Date**: March 15, 2024
2006
+
2007
+ **Review Questions**:
2008
+ - \u2713 Relevance: Still critical path item
2009
+ - \u26A0 Timeline: 5 days remaining, 60% complete
2010
+ - Progress: 3 of 5 design tasks done
2011
+ - Risks: User research taking longer than expected
2012
+
2013
+ **Decision**: Extend target date by 3 days, add resources to user research
2014
+
2015
+ ## Review Schedule Recommendations
2016
+
2017
+ ### Weekly
2018
+ - Quick check of upcoming milestones (next 2 weeks)
2019
+ - Identify any at-risk items
2020
+
2021
+ ### Monthly
2022
+ - Full review of all active milestones
2023
+ - Adjust timelines as needed
2024
+ - Celebrate completed milestones
2025
+
2026
+ ### Quarterly
2027
+ - Strategic review of milestone alignment
2028
+ - Archive completed project milestones
2029
+ - Plan next quarter's milestones
2030
+
2031
+ ## Metrics to Track
2032
+
2033
+ - **Milestone Completion Rate**: % of milestones completed on time
2034
+ - **Average Delay**: How often and by how much dates slip
2035
+ - **Scope Changes**: Number of milestones added/removed
2036
+ - **Predictability**: Variance between planned and actual dates
2037
+
2038
+ What would you like to do?
2039
+ - Review milestones for a specific project
2040
+ - Identify at-risk milestones
2041
+ - Plan new milestones
2042
+ - Analyze milestone performance
2043
+ - Update existing milestones`
2044
+ }
2045
+ }
2046
+ ]
2047
+ };
2048
+ }
2049
+ function getPromptByName(name, args) {
2050
+ switch (name) {
2051
+ case "projectPlanningPrompt":
2052
+ return getProjectPlanningPrompt(args?.projectType);
2053
+ case "taskManagementPrompt":
2054
+ return getTaskManagementPrompt(args?.projectId);
2055
+ case "roadmapOverviewPrompt":
2056
+ return getRoadmapOverviewPrompt();
2057
+ case "milestoneReviewPrompt":
2058
+ return getMilestoneReviewPrompt(args?.projectId);
2059
+ default:
2060
+ return null;
2061
+ }
2062
+ }
2063
+ function getAllPrompts() {
2064
+ return projectPrompts;
2065
+ }
2066
+
2067
+ // src/server.ts
2068
+ var allTools = [
2069
+ createProjectTool,
2070
+ listProjectsTool,
2071
+ getProjectTool,
2072
+ updateProjectTool,
2073
+ deleteProjectTool,
2074
+ createTaskTool,
2075
+ listTasksTool,
2076
+ getTaskTool,
2077
+ updateTaskTool,
2078
+ deleteTaskTool,
2079
+ batchUpdateTasksTool,
2080
+ createTagTool,
2081
+ listTagsTool,
2082
+ updateTagTool,
2083
+ deleteTagTool,
2084
+ getTasksByTagTool,
2085
+ openWebInterfaceTool,
2086
+ closeWebInterfaceTool
2087
+ ];
2088
+ var toolMap = new Map(allTools.map((tool) => [tool.name, tool]));
2089
+ function createServer2() {
2090
+ const server = new Server(
2091
+ {
2092
+ name: "roadmap-skill",
2093
+ version: "0.1.0"
2094
+ },
2095
+ {
2096
+ capabilities: {
2097
+ tools: {},
2098
+ resources: {},
2099
+ prompts: {}
2100
+ }
2101
+ }
2102
+ );
2103
+ server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
2104
+ return {
2105
+ tools: allTools.map((tool) => {
2106
+ const inputSchema = tool.parameters || tool.inputSchema;
2107
+ return {
2108
+ name: tool.name,
2109
+ description: tool.description,
2110
+ inputSchema: inputSchema || { type: "object" }
2111
+ };
2112
+ })
2113
+ };
2114
+ });
2115
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2116
+ const { name, arguments: args } = request.params;
2117
+ const tool = toolMap.get(name);
2118
+ if (!tool) {
2119
+ throw new Error(`Unknown tool: ${name}`);
2120
+ }
2121
+ try {
2122
+ const result = await tool.execute(args);
2123
+ return {
2124
+ content: [
2125
+ {
2126
+ type: "text",
2127
+ text: JSON.stringify(result, null, 2)
2128
+ }
2129
+ ]
2130
+ };
2131
+ } catch (error) {
2132
+ const errorMessage = error instanceof Error ? error.message : String(error);
2133
+ throw new Error(`Tool execution failed: ${errorMessage}`);
2134
+ }
2135
+ });
2136
+ server.setRequestHandler(ListResourcesRequestSchema, async (_request) => {
2137
+ const resources = getAllResources();
2138
+ return { resources };
2139
+ });
2140
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2141
+ const uri = request.params.uri;
2142
+ const resourceContent = await handleResourceRequest(uri);
2143
+ if (!resourceContent) {
2144
+ throw new Error(`Resource not found: ${uri}`);
2145
+ }
2146
+ return {
2147
+ contents: [resourceContent]
2148
+ };
2149
+ });
2150
+ server.setRequestHandler(ListPromptsRequestSchema, async (_request) => {
2151
+ const prompts = getAllPrompts();
2152
+ return { prompts };
2153
+ });
2154
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
2155
+ const { name, arguments: args } = request.params;
2156
+ const promptResult = getPromptByName(name, args);
2157
+ if (!promptResult) {
2158
+ throw new Error(`Prompt not found: ${name}`);
2159
+ }
2160
+ return promptResult;
2161
+ });
2162
+ return server;
2163
+ }
2164
+ async function startServer() {
2165
+ const server = createServer2();
2166
+ const transport = new StdioServerTransport();
2167
+ console.error("Roadmap Skill MCP Server starting...");
2168
+ try {
2169
+ await server.connect(transport);
2170
+ console.error("Roadmap Skill MCP Server connected and ready");
2171
+ } catch (error) {
2172
+ console.error("Failed to start server:", error);
2173
+ throw error;
2174
+ }
2175
+ }
2176
+
2177
+ // src/index.ts
2178
+ async function main() {
2179
+ process.on("SIGINT", () => {
2180
+ console.error("Received SIGINT, shutting down gracefully...");
2181
+ process.exit(0);
2182
+ });
2183
+ process.on("SIGTERM", () => {
2184
+ console.error("Received SIGTERM, shutting down gracefully...");
2185
+ process.exit(0);
2186
+ });
2187
+ process.on("uncaughtException", (error) => {
2188
+ console.error("Uncaught exception:", error);
2189
+ process.exit(1);
2190
+ });
2191
+ process.on("unhandledRejection", (reason) => {
2192
+ console.error("Unhandled rejection:", reason);
2193
+ process.exit(1);
2194
+ });
2195
+ try {
2196
+ await startServer();
2197
+ } catch (error) {
2198
+ console.error("Failed to start server:", error);
2199
+ process.exit(1);
2200
+ }
2201
+ }
2202
+ main();
2203
+ //# sourceMappingURL=index.js.map